mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2024-11-19 18:00:17 +01:00
Merge remote-tracking branch 'origin/master' into kbtype
This commit is contained in:
commit
b87b510c1c
@ -44,7 +44,6 @@ import Lnurl from './class/lnurl';
|
||||
import { BlueStorageContext } from './blue_modules/storage-context';
|
||||
import ToolTipMenu from './components/TooltipMenu';
|
||||
|
||||
/** @type {AppStorage} */
|
||||
const { height, width } = Dimensions.get('window');
|
||||
const aspectRatio = height / width;
|
||||
let isIpad;
|
||||
|
@ -7,7 +7,6 @@ import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { StackActions, useNavigation, useRoute } from '@react-navigation/native';
|
||||
import { BlueStorageContext } from './blue_modules/storage-context';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
/** @type {AppStorage} */
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
|
@ -13,7 +13,7 @@ if [ -f $FILENAME ]; then
|
||||
APTZ=`curl "https://$APPETIZE@api.appetize.io/v1/apps" -F "file=@$FILENAME" -F "platform=android"`
|
||||
echo Apptezize response:
|
||||
echo $APTZ
|
||||
APPURL=`node -e "let e = JSON.parse('$APTZ'); console.log(e.publicURL);"`
|
||||
APPURL=`node -e "let e = JSON.parse('$APTZ'); console.log(e.publicURL + '?device=pixel4');"`
|
||||
echo App url: $APPURL
|
||||
PR=`node scripts/appcenter-post-build-get-pr-number.js`
|
||||
echo PR: $PR
|
||||
|
@ -10,6 +10,34 @@ const ElectrumClient = require('electrum-client');
|
||||
const reverse = require('buffer-reverse');
|
||||
const BigNumber = require('bignumber.js');
|
||||
const torrific = require('../blue_modules/torrific');
|
||||
const Realm = require('realm');
|
||||
|
||||
let _realm;
|
||||
async function _getRealm() {
|
||||
if (_realm) return _realm;
|
||||
|
||||
const password = bitcoin.crypto.sha256(Buffer.from('fyegjitkyf[eqjnc.lf')).toString('hex');
|
||||
const buf = Buffer.from(password + password, 'hex');
|
||||
const encryptionKey = Int8Array.from(buf);
|
||||
const path = 'electrumcache.realm';
|
||||
|
||||
const schema = [
|
||||
{
|
||||
name: 'Cache',
|
||||
primaryKey: 'cache_key',
|
||||
properties: {
|
||||
cache_key: { type: 'string', indexed: true },
|
||||
cache_value: 'string', // stringified json
|
||||
},
|
||||
},
|
||||
];
|
||||
_realm = await Realm.open({
|
||||
schema,
|
||||
path,
|
||||
encryptionKey,
|
||||
});
|
||||
return _realm;
|
||||
}
|
||||
|
||||
const storageKey = 'ELECTRUM_PEERS';
|
||||
const defaultPeer = { host: 'electrum1.bluewallet.io', ssl: '443' };
|
||||
@ -470,15 +498,38 @@ module.exports.multiGetHistoryByAddress = async function (addresses, batchsize)
|
||||
return ret;
|
||||
};
|
||||
|
||||
module.exports.multiGetTransactionByTxid = async function (txids, batchsize, verbose) {
|
||||
module.exports.multiGetTransactionByTxid = async function (txids, batchsize, verbose = true) {
|
||||
batchsize = batchsize || 45;
|
||||
// this value is fine-tuned so althrough wallets in test suite will occasionally
|
||||
// throw 'response too large (over 1,000,000 bytes', test suite will pass
|
||||
verbose = verbose !== false;
|
||||
if (!mainClient) throw new Error('Electrum client is not connected');
|
||||
const ret = {};
|
||||
txids = [...new Set(txids)]; // deduplicate just for any case
|
||||
|
||||
// lets try cache first:
|
||||
const realm = await _getRealm();
|
||||
const cacheKeySuffix = verbose ? '_verbose' : '_non_verbose';
|
||||
const keysCacheMiss = [];
|
||||
for (const txid of txids) {
|
||||
const jsonString = realm.objectForPrimaryKey('Cache', txid + cacheKeySuffix); // search for a realm object with a primary key
|
||||
if (jsonString && jsonString.cache_value) {
|
||||
try {
|
||||
ret[txid] = JSON.parse(jsonString.cache_value);
|
||||
} catch (error) {
|
||||
console.log(error, 'cache failed to parse', jsonString.cache_value);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret[txid]) keysCacheMiss.push(txid);
|
||||
}
|
||||
|
||||
if (keysCacheMiss.length === 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
txids = keysCacheMiss;
|
||||
// end cache
|
||||
|
||||
const chunks = splitIntoChunks(txids, batchsize);
|
||||
for (const chunk of chunks) {
|
||||
let results = [];
|
||||
@ -522,6 +573,23 @@ module.exports.multiGetTransactionByTxid = async function (txids, batchsize, ver
|
||||
}
|
||||
}
|
||||
|
||||
// saving cache:
|
||||
realm.write(() => {
|
||||
for (const txid of Object.keys(ret)) {
|
||||
if (verbose && (!ret[txid].confirmations || ret[txid].confirmations < 7)) continue;
|
||||
// dont cache immature txs, but only for 'verbose', since its fully decoded tx jsons. non-verbose are just plain
|
||||
// strings txhex
|
||||
realm.create(
|
||||
'Cache',
|
||||
{
|
||||
cache_key: txid + cacheKeySuffix,
|
||||
cache_value: JSON.stringify(ret[txid]),
|
||||
},
|
||||
Realm.UpdateMode.Modified,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
@ -106,7 +106,7 @@ const showImagePickerAndReadImage = () => {
|
||||
},
|
||||
response => {
|
||||
if (response.uri) {
|
||||
const uri = Platform.OS === 'ios' ? response.uri.toString().replace('file://', '') : response.path.toString();
|
||||
const uri = response.uri.toString().replace('file://', '');
|
||||
LocalQRCode.decode(uri, (error, result) => {
|
||||
if (!error) {
|
||||
resolve(result);
|
||||
@ -130,7 +130,7 @@ const takePhotoWithImagePickerAndReadPhoto = () => {
|
||||
},
|
||||
response => {
|
||||
if (response.uri) {
|
||||
const uri = Platform.OS === 'ios' ? response.uri.toString().replace('file://', '') : response.path.toString();
|
||||
const uri = response.uri.toString().replace('file://', '');
|
||||
LocalQRCode.decode(uri, (error, result) => {
|
||||
if (!error) {
|
||||
resolve(result);
|
||||
@ -174,7 +174,7 @@ const showFilePickerAndReadFile = async function () {
|
||||
|
||||
if (res?.type === DocumentPicker.types.images || res?.type?.startsWith('image/')) {
|
||||
return new Promise(resolve => {
|
||||
const uri = Platform.OS === 'ios' ? res.uri.toString().replace('file://', '') : res.uri;
|
||||
const uri = res.uri.toString().replace('file://', '');
|
||||
LocalQRCode.decode(decodeURI(uri), (error, result) => {
|
||||
if (!error) {
|
||||
resolve({ data: result, uri: decodeURI(res.uri) });
|
||||
|
@ -43,16 +43,6 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
||||
return ret + (this.getUnconfirmedBalance() < 0 ? this.getUnconfirmedBalance() : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
timeToRefreshTransaction() {
|
||||
for (const tx of this.getTransactions()) {
|
||||
if (tx.confirmations < 7) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @inheritDoc
|
||||
|
@ -37,7 +37,7 @@ export class LegacyWallet extends AbstractWallet {
|
||||
*/
|
||||
timeToRefreshTransaction() {
|
||||
for (const tx of this.getTransactions()) {
|
||||
if (tx.confirmations < 7) {
|
||||
if (tx.confirmations < 7 && this._lastTxFetch < +new Date() - 5 * 60 * 1000) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,16 @@ export class WatchOnlyWallet extends LegacyWallet {
|
||||
return super.getLastTxFetch();
|
||||
}
|
||||
|
||||
timeToRefreshTransaction() {
|
||||
if (this._hdWalletInstance) return this._hdWalletInstance.timeToRefreshTransaction();
|
||||
return super.timeToRefreshTransaction();
|
||||
}
|
||||
|
||||
timeToRefreshBalance() {
|
||||
if (this._hdWalletInstance) return this._hdWalletInstance.timeToRefreshBalance();
|
||||
return super.timeToRefreshBalance();
|
||||
}
|
||||
|
||||
allowSend() {
|
||||
return this.useWithHardwareWalletEnabled() && this.isHd() && this._hdWalletInstance.allowSend();
|
||||
}
|
||||
|
@ -276,7 +276,7 @@
|
||||
"electrum_history": "Server history",
|
||||
"electrum_reset_to_default": "Are you sure to want to reset your Electrum settings to default?",
|
||||
"electrum_clear": "Clear",
|
||||
"tor_supported": "TOR supported",
|
||||
"tor_supported": "Tor supported",
|
||||
"encrypt_decrypt": "Decrypt Storage",
|
||||
"encrypt_decrypt_q": "Are you sure you want to decrypt your storage? This will allow your wallets to be accessed without a password.",
|
||||
"encrypt_del_uninstall": "Delete if BlueWallet is uninstalled",
|
||||
@ -297,7 +297,7 @@
|
||||
"lightning_error_lndhub_uri": "Not a valid LNDHub URI",
|
||||
"lightning_saved": "Your changes have been saved successfully.",
|
||||
"lightning_settings": "Lightning Settings",
|
||||
"tor_settings": "TOR Settings",
|
||||
"tor_settings": "Tor Settings",
|
||||
"lightning_settings_explain": "To connect to your own LND node, please install LNDHub and put its URL here in settings. Leave blank to use BlueWallet’s LNDHub (lndhub.io). Wallets created after saving changes will connect to the specified LNDHub.",
|
||||
"network": "Network",
|
||||
"network_broadcast": "Broadcast Transaction",
|
||||
|
@ -276,6 +276,7 @@
|
||||
"electrum_history": "تاریخچهٔ سرورها",
|
||||
"electrum_reset_to_default": "آیا مطمئن هستید که میخواهید تنظیمات الکترام را به حالت پیشفرض بازنشانی کنید؟",
|
||||
"electrum_clear": "پاککردن",
|
||||
"tor_supported": "پشتیبانی از تور",
|
||||
"encrypt_decrypt": "رمزگشایی فضای ذخیرهسازی",
|
||||
"encrypt_decrypt_q": "آیا مطمئن هستید که میخواهید فضای ذخیرهسازی خود را رمزگشایی کنید؟ این کار اجازه میدهد تا کیف پولهای شما بدون گذرواژه قابلدسترسی باشند.",
|
||||
"encrypt_del_uninstall": "درصورت لغو نصب BlueWallet، حذف شود",
|
||||
@ -296,6 +297,7 @@
|
||||
"lightning_error_lndhub_uri": "یوآرآی LNDHub معتبر نیست",
|
||||
"lightning_saved": "تغییرات شما با موفقیت ذخیره شدند.",
|
||||
"lightning_settings": "تنظیمات لایتنینگ",
|
||||
"tor_settings": "تنظیمات تور",
|
||||
"lightning_settings_explain": "برای اتصال به گره LND خود، لطفاً LNDHub را نصب کرده و آدرس آن را اینجا در تنظیمات قرار دهید. برای استفاده از LNDHub برنامهٔ BlueWallet (به آدرس lndhub.io)، خالی بگذارید. کیف پولهای ایجادشده بعد از ذخیرهٔ تغییرات به LNDHub مشخصشده متصل خواهند شد.",
|
||||
"network": "شبکه",
|
||||
"network_broadcast": "انتشار تراکنش",
|
||||
|
@ -14,6 +14,7 @@
|
||||
"no": "Ne",
|
||||
"save": "Shrani",
|
||||
"seed": "Seme",
|
||||
"success": "Uspešno",
|
||||
"wallet_key": "Ključ denarnice",
|
||||
"invalid_animated_qr_code_fragment" : "Neveljaven del animirane QR kode. Prosimo poskusite ponovno.",
|
||||
"file_saved": "Datoteka ({filePath}) je bila shranjena v mapo Prenosi.",
|
||||
@ -135,9 +136,9 @@
|
||||
"ask": "Ali ste shranili varnostno kopijo (seznam besed) vaše denarnice? Varnostna kopija je potrebna za dostop do vaših sredstev v primeru izgube naprave. Brez varnostne kopije bodo vaša sredstva trajno izgubljena.",
|
||||
"ask_no": "Ne, nisem",
|
||||
"ask_yes": "Da, sem",
|
||||
"ok": "V redu, sem si zapisal!",
|
||||
"ok_lnd": "V redu, sem shranil.",
|
||||
"text": "Prosimo zapišite si seznam besed (mnemonično seme) na list papirja. To je varnostna kopija, ki jo lahko uporabite za obnovitev denarnice na drugi napravi.",
|
||||
"ok": "V redu, sem si zapisal",
|
||||
"ok_lnd": "V redu, sem shranil",
|
||||
"text": "Prosimo zapišite si seznam besed (mnemonično seme) na list papirja.\nTo je varnostna kopija, ki jo lahko uporabite za obnovitev denarnice.",
|
||||
"text_lnd": "Shranite varnostno kopijo te denarnice. Omogoča vam obnovitev denarnice v primeru izgube te naprave.",
|
||||
"text_lnd2": "Ta denarnica uporablja gostovanje BlueWallet.",
|
||||
"title": "Vaša denarnica je ustvarjena"
|
||||
@ -174,7 +175,6 @@
|
||||
"details_address_field_is_not_valid": "Naslov ni veljaven",
|
||||
"details_adv_fee_bump": "Omogoči Povečanje Omrežnine",
|
||||
"details_adv_full": "Uporabi Celotno Stanje",
|
||||
"details_adv_full_remove": "Drugi prejemniki bodo odstranjeni iz te transakcije.",
|
||||
"details_adv_full_sure": "Ali ste prepričani, da želite za to transakcijo uporabiti celotno stanje denarnice?",
|
||||
"details_adv_import": "Uvozi transakcijo",
|
||||
"details_amount_field_is_not_valid": "Znesek ni veljaven",
|
||||
@ -183,15 +183,12 @@
|
||||
"details_error_decode": "Ni mogoče dekodirati Bitcoin naslova",
|
||||
"details_fee_field_is_not_valid": "Omrežnina ni veljavna",
|
||||
"details_next": "Naprej",
|
||||
"details_no_maximum": "Izbrana denarnica ne podpira samodejnega izračuna največjega stanja. Ali ste prepričani, da želite izbrati to denarnico?",
|
||||
"details_no_multiple": "Izbrana denarnica ne podpira pošiljanja več prejemnikom. Ali ste prepričani, da želite izbrati to denarnico?",
|
||||
"details_no_signed_tx": "Izbrana datoteka ne vsebuje transakcije, ki jo je mogoče uvoziti.",
|
||||
"details_note_placeholder": "lastna opomba",
|
||||
"details_scan": "Skeniraj",
|
||||
"details_total_exceeds_balance": "Znesek presega razpoložljivo stanje.",
|
||||
"details_unrecognized_file_format": "Neprepoznana oblika datoteke",
|
||||
"details_wallet_before_tx": "Pred ustvarjanjem transakcije, morate dodati Bitcoin denarnico.",
|
||||
"details_wallet_selection": "Izbira Denarnice",
|
||||
"dynamic_init": "Inicializacija",
|
||||
"dynamic_next": "Naprej",
|
||||
"dynamic_prev": "Nazaj",
|
||||
@ -279,6 +276,7 @@
|
||||
"electrum_history": "Zgodovina strežnikov",
|
||||
"electrum_reset_to_default": "Ali ste prepričani, da želite ponastaviti nastavitve Electrum strežnika na privzeto?",
|
||||
"electrum_clear": "Počisti",
|
||||
"tor_supported": "TOR podprt",
|
||||
"encrypt_decrypt": "Dešifriraj Shrambo",
|
||||
"encrypt_decrypt_q": "Ali ste prepričani, da želite dešifrirati shrambo? To bo omogočilo dostop do vaših denarnic brez gesla.",
|
||||
"encrypt_del_uninstall": "Izbriši, če je BlueWallet odstranjen",
|
||||
@ -295,10 +293,11 @@
|
||||
"groundcontrol_explanation": "GroundControl je brezplačen odprtokoden strežnik potisnih obvestil za bitcoin denarnice. Da se ne zanašate na BlueWallet infrastrukturo, lahko namestite svoj strežnik GroundControl in tukaj dodate njegov URL. Pustite prazno, za uporabo privzetega.",
|
||||
"header": "Nastavitve",
|
||||
"language": "Jezik",
|
||||
"language_restart": "Pri izbiri novega jezika bo morda potrebno ponovno zagnati BlueWallet, da bo sprememba začela veljati.",
|
||||
"language_isRTL": "Za spremembo orientacije pisave je potreben ponovni zagon aplikacije.",
|
||||
"lightning_error_lndhub_uri": "Neveljaven LndHub URI",
|
||||
"lightning_saved": "Spremembe so bile uspešno shranjene",
|
||||
"lightning_settings": "Lightning Nastavitve",
|
||||
"tor_settings": "TOR Nastavitve",
|
||||
"lightning_settings_explain": "Za povezavo z lastnim LND vozliščem, prosimo namestite LndHub in tukaj vnesite URL vozlišča. Pustite prazno za uporabo BlueWallet LNDHub (lndhub.io). Denarnice ustvarjene po potrditvi sprememb bodo povezane z novim LNDHub-om.",
|
||||
"network": "Omrežje",
|
||||
"network_broadcast": "Objavi transakcijo",
|
||||
@ -325,7 +324,8 @@
|
||||
"success_transaction_broadcasted" : "Vaša transakcija je bila objavljena!",
|
||||
"total_balance": "Skupno stanje",
|
||||
"total_balance_explanation": "Prikaži skupno stanje vseh denarnic na pripomočkih na domačem zaslonu.",
|
||||
"widgets": "Pripomočki"
|
||||
"widgets": "Pripomočki",
|
||||
"tools": "Orodja"
|
||||
},
|
||||
"notifications": {
|
||||
"would_you_like_to_receive_notifications": "Želite prikaz obvestil ob prejemu plačila?",
|
||||
@ -409,6 +409,7 @@
|
||||
"details_no_cancel": "Ne, prekliči",
|
||||
"details_save": "Shrani",
|
||||
"details_show_xpub": "Prikaži XPUB denarnice",
|
||||
"details_show_addresses": "Prikaži naslove",
|
||||
"details_title": "Denarnica",
|
||||
"details_type": "Tip",
|
||||
"details_use_with_hardware_wallet": "Uporaba s strojno denarnico",
|
||||
@ -561,5 +562,28 @@
|
||||
"MAX": "MAX",
|
||||
"sat_byte": "sat/bajt",
|
||||
"sats": "sats"
|
||||
},
|
||||
"addresses": {
|
||||
"sign_title": "Podpiši / Preveri sporočilo",
|
||||
"sign_help": "Ustvarite ali preverite kriptografski podpis na podlagi Bitcoin naslova",
|
||||
"sign_sign": "Podpiši",
|
||||
"sign_sign_submit": "Podpiši in Oddaj",
|
||||
"sign_verify": "Preveri",
|
||||
"sign_signature_correct": "Preverjanje uspešno!",
|
||||
"sign_signature_incorrect": "Preverjanje neuspešno!",
|
||||
"sign_placeholder_address": "Naslov",
|
||||
"sign_placeholder_message": "Sporočilo",
|
||||
"sign_placeholder_signature": "Podpis",
|
||||
"sign_aopp_title": "AOPP",
|
||||
"sign_aopp_confirm": "Ali želite podpisano sporočilo poslati na {hostname}?",
|
||||
"address_balance": "Stanje: {balance} sats",
|
||||
"addresses_title": "Naslovi",
|
||||
"type_change": "Vračilo",
|
||||
"type_receive": "Prejemni"
|
||||
},
|
||||
"aopp": {
|
||||
"title": "Izberite Naslov",
|
||||
"send_success": "Podpis uspešno poslan",
|
||||
"send_error": "Napaka pri pošiljanju podpisa"
|
||||
}
|
||||
}
|
||||
|
@ -215,7 +215,7 @@ const ScanQRCode = () => {
|
||||
setIsLoading(false);
|
||||
} else {
|
||||
if (response.uri) {
|
||||
const uri = Platform.OS === 'ios' ? response.uri.toString().replace('file://', '') : response.uri;
|
||||
const uri = response.uri.toString().replace('file://', '');
|
||||
LocalQRCode.decode(uri, (error, result) => {
|
||||
if (!error) {
|
||||
onBarCodeRead({ data: result });
|
||||
|
@ -443,7 +443,7 @@ const SendDetails = () => {
|
||||
const changeAddress = await getChangeAddressAsync();
|
||||
const requestedSatPerByte = Number(feeRate);
|
||||
const lutxo = utxo || wallet.getUtxo();
|
||||
console.log({ requestedSatPerByte, utxo });
|
||||
console.log({ requestedSatPerByte, lutxo: lutxo.length });
|
||||
|
||||
const targets = [];
|
||||
for (const transaction of addresses) {
|
||||
|
@ -37,7 +37,6 @@ import loc from '../../loc';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import Notifications from '../../blue_modules/notifications';
|
||||
const BlueElectrum = require('../../blue_modules/BlueElectrum');
|
||||
/** @type {AppStorage} */
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
const fs = require('../../blue_modules/fs');
|
||||
|
||||
|
@ -76,14 +76,15 @@ const TorSettings = () => {
|
||||
<BlueCard>
|
||||
<BlueText>Daemon Status: {daemonStatus}</BlueText>
|
||||
</BlueCard>
|
||||
|
||||
<BlueButton title="start" onPress={startIfNotStarted} />
|
||||
<BlueSpacing20 />
|
||||
<BlueButton title="stop" onPress={stopIfRunning} />
|
||||
<BlueSpacing20 />
|
||||
<BlueButton title="test socket" onPress={testSocket} />
|
||||
<BlueSpacing20 />
|
||||
<BlueButton title="test http" onPress={testHttp} />
|
||||
<BlueCard>
|
||||
<BlueButton title="start" onPress={startIfNotStarted} />
|
||||
<BlueSpacing20 />
|
||||
<BlueButton title="stop" onPress={stopIfRunning} />
|
||||
<BlueSpacing20 />
|
||||
<BlueButton title="test socket" onPress={testSocket} />
|
||||
<BlueSpacing20 />
|
||||
<BlueButton title="test http" onPress={testHttp} />
|
||||
</BlueCard>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
};
|
||||
|
@ -3,7 +3,6 @@ import { Image, View, Text, ScrollView, StyleSheet } from 'react-native';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
import { SafeBlueArea, BlueLoading } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
/** @type {AppStorage} */
|
||||
import loc from '../../loc';
|
||||
|
||||
const WalletsAddMultisigHelp = () => {
|
||||
|
@ -463,7 +463,7 @@ const WalletTransactions = () => {
|
||||
},
|
||||
response => {
|
||||
if (response.uri) {
|
||||
const uri = Platform.OS === 'ios' ? response.uri.toString().replace('file://', '') : response.uri;
|
||||
const uri = response.uri.toString().replace('file://', '');
|
||||
LocalQRCode.decode(uri, (error, result) => {
|
||||
if (!error) {
|
||||
onBarCodeRead({ data: result });
|
||||
|
@ -95,6 +95,8 @@ jest.mock('react-native-haptic-feedback', () => ({}));
|
||||
|
||||
const realmInstanceMock = {
|
||||
close: function () {},
|
||||
write: function () {},
|
||||
objectForPrimaryKey: function () { return {}; },
|
||||
objects: function () {
|
||||
const wallets = {
|
||||
filtered: function () {
|
||||
|
Loading…
Reference in New Issue
Block a user