mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-22 23:08:07 +01:00
DEL: Remove ldk
This commit is contained in:
parent
702289ffb2
commit
1ef78c82d7
30 changed files with 24 additions and 3010 deletions
10
BlueApp.js
10
BlueApp.js
|
@ -18,7 +18,6 @@ import {
|
|||
HDSegwitElectrumSeedP2WPKHWallet,
|
||||
HDAezeedWallet,
|
||||
MultisigHDWallet,
|
||||
LightningLdkWallet,
|
||||
SLIP39SegwitP2SHWallet,
|
||||
SLIP39LegacyP2PKHWallet,
|
||||
SLIP39SegwitBech32Wallet,
|
||||
|
@ -381,9 +380,6 @@ class AppStorage {
|
|||
unserializedWallet.passphrase = passphrase;
|
||||
}
|
||||
|
||||
break;
|
||||
case LightningLdkWallet.type:
|
||||
unserializedWallet = LightningLdkWallet.fromJson(key);
|
||||
break;
|
||||
case SLIP39SegwitP2SHWallet.type:
|
||||
unserializedWallet = SLIP39SegwitP2SHWallet.fromJson(key);
|
||||
|
@ -452,12 +448,6 @@ class AppStorage {
|
|||
const ID = wallet.getID();
|
||||
const tempWallets = [];
|
||||
|
||||
if (wallet.type === LightningLdkWallet.type) {
|
||||
/** @type {LightningLdkWallet} */
|
||||
const ldkwallet = wallet;
|
||||
ldkwallet.stop().then(ldkwallet.purgeLocalStorage).catch(alert);
|
||||
}
|
||||
|
||||
for (const value of this.wallets) {
|
||||
if (value.getID() === ID) {
|
||||
// the one we should delete
|
||||
|
|
|
@ -36,7 +36,6 @@ import ImportSpeed from './screen/wallets/importSpeed';
|
|||
import WalletsList from './screen/wallets/list';
|
||||
import PleaseBackup from './screen/wallets/pleaseBackup';
|
||||
import PleaseBackupLNDHub from './screen/wallets/pleaseBackupLNDHub';
|
||||
import PleaseBackupLdk from './screen/wallets/pleaseBackupLdk';
|
||||
import ProvideEntropy from './screen/wallets/provideEntropy';
|
||||
import ReorderWallets from './screen/wallets/reorderWallets';
|
||||
import SelectWallet from './screen/wallets/selectWallet';
|
||||
|
@ -72,8 +71,6 @@ import navigationStyle from './components/navigationStyle';
|
|||
import { useTheme } from './components/themes';
|
||||
import loc from './loc';
|
||||
import LappBrowser from './screen/lnd/browser';
|
||||
import LdkInfo from './screen/lnd/ldkInfo';
|
||||
import LdkOpenChannel from './screen/lnd/ldkOpenChannel';
|
||||
import LNDCreateInvoice from './screen/lnd/lndCreateInvoice';
|
||||
import LNDViewAdditionalInvoiceInformation from './screen/lnd/lndViewAdditionalInvoiceInformation';
|
||||
import LNDViewAdditionalInvoicePreImage from './screen/lnd/lndViewAdditionalInvoicePreImage';
|
||||
|
@ -84,7 +81,6 @@ import LnurlPaySuccess from './screen/lnd/lnurlPaySuccess';
|
|||
import ScanLndInvoice from './screen/lnd/scanLndInvoice';
|
||||
import SettingsPrivacy from './screen/settings/SettingsPrivacy';
|
||||
import DrawerList from './screen/wallets/drawerList';
|
||||
import LdkViewLogs from './screen/wallets/ldkViewLogs';
|
||||
import PaymentCode from './screen/wallets/paymentCode';
|
||||
import PaymentCodesList from './screen/wallets/paymentCodesList';
|
||||
|
||||
|
@ -97,10 +93,7 @@ const WalletsRoot = () => {
|
|||
<WalletsStack.Navigator screenOptions={{ headerShadowVisible: false }}>
|
||||
<WalletsStack.Screen name="WalletsList" component={WalletsList} options={WalletsList.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="WalletTransactions" component={WalletTransactions} options={WalletTransactions.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="LdkOpenChannel" component={LdkOpenChannel} options={LdkOpenChannel.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="LdkInfo" component={LdkInfo} options={LdkInfo.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="WalletDetails" component={WalletDetails} options={WalletDetails.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="LdkViewLogs" component={LdkViewLogs} options={LdkViewLogs.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="TransactionDetails" component={TransactionDetails} options={TransactionDetails.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="TransactionStatus" component={TransactionStatus} options={TransactionStatus.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="CPFP" component={CPFP} options={CPFP.navigationOptions(theme)} />
|
||||
|
@ -207,7 +200,6 @@ const AddWalletRoot = () => {
|
|||
component={PleaseBackupLNDHub}
|
||||
options={PleaseBackupLNDHub.navigationOptions(theme)}
|
||||
/>
|
||||
<AddWalletStack.Screen name="PleaseBackupLdk" component={PleaseBackupLdk} options={PleaseBackupLdk.navigationOptions(theme)} />
|
||||
<AddWalletStack.Screen name="ProvideEntropy" component={ProvideEntropy} options={ProvideEntropy.navigationOptions(theme)} />
|
||||
<AddWalletStack.Screen
|
||||
name="WalletsAddMultisig"
|
||||
|
@ -317,23 +309,6 @@ const ScanLndInvoiceRoot = () => {
|
|||
);
|
||||
};
|
||||
|
||||
const LDKOpenChannelStack = createNativeStackNavigator();
|
||||
const LDKOpenChannelRoot = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<LDKOpenChannelStack.Navigator id="LDKOpenChannelRoot" screenOptions={{ headerShadowVisible: false }} initialRouteName="SelectWallet">
|
||||
<LDKOpenChannelStack.Screen name="SelectWallet" component={SelectWallet} options={SelectWallet.navigationOptions(theme)} />
|
||||
<LDKOpenChannelStack.Screen
|
||||
name="LDKOpenChannelSetAmount"
|
||||
component={LdkOpenChannel}
|
||||
options={LdkOpenChannel.navigationOptions(theme)}
|
||||
/>
|
||||
<LDKOpenChannelStack.Screen name="Success" component={Success} options={{ headerShown: false, gestureEnabled: false }} />
|
||||
</LDKOpenChannelStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const AztecoRedeemStack = createNativeStackNavigator();
|
||||
const AztecoRedeemRoot = () => {
|
||||
const theme = useTheme();
|
||||
|
@ -609,7 +584,6 @@ const Navigation = () => {
|
|||
<RootStack.Screen name="SelectWallet" component={SelectWallet} />
|
||||
<RootStack.Screen name="ReceiveDetailsRoot" component={ReceiveDetailsStackRoot} options={NavigationDefaultOptions} />
|
||||
<RootStack.Screen name="LappBrowserRoot" component={LappBrowserStackRoot} options={NavigationDefaultOptions} />
|
||||
<RootStack.Screen name="LDKOpenChannelRoot" component={LDKOpenChannelRoot} options={NavigationDefaultOptions} />
|
||||
|
||||
<RootStack.Screen
|
||||
name="ScanQRCodeRoot"
|
||||
|
|
|
@ -98,7 +98,6 @@ android {
|
|||
dependencies {
|
||||
// The version of react-native is set by the React Native Gradle Plugin
|
||||
implementation("com.facebook.react:react-android")
|
||||
implementation files("../../node_modules/rn-ldk/android/libs/LDK-release.aar")
|
||||
|
||||
if (hermesEnabled.toBoolean()) {
|
||||
implementation("com.facebook.react:hermes-android")
|
||||
|
|
|
@ -10,7 +10,6 @@ import { HDLegacyElectrumSeedP2PKHWallet } from './wallets/hd-legacy-electrum-se
|
|||
import { HDSegwitElectrumSeedP2WPKHWallet } from './wallets/hd-segwit-electrum-seed-p2wpkh-wallet';
|
||||
import { MultisigHDWallet } from './wallets/multisig-hd-wallet';
|
||||
import { HDAezeedWallet } from './wallets/hd-aezeed-wallet';
|
||||
import { LightningLdkWallet } from './wallets/lightning-ldk-wallet';
|
||||
import { SLIP39LegacyP2PKHWallet, SLIP39SegwitP2SHWallet, SLIP39SegwitBech32Wallet } from './wallets/slip39-wallets';
|
||||
import { useTheme } from '../components/themes';
|
||||
|
||||
|
@ -68,9 +67,6 @@ export default class WalletGradient {
|
|||
case HDAezeedWallet.type:
|
||||
gradient = WalletGradient.aezeedWallet;
|
||||
break;
|
||||
case LightningLdkWallet.type:
|
||||
gradient = WalletGradient.ldkWallet;
|
||||
break;
|
||||
case LightningCustodianWallet.type:
|
||||
gradient = WalletGradient.lightningCustodianWallet;
|
||||
break;
|
||||
|
@ -131,9 +127,6 @@ export default class WalletGradient {
|
|||
case HDAezeedWallet.type:
|
||||
gradient = WalletGradient.aezeedWallet;
|
||||
break;
|
||||
case LightningLdkWallet.type:
|
||||
gradient = WalletGradient.ldkWallet;
|
||||
break;
|
||||
case LightningCustodianWallet.type:
|
||||
gradient = WalletGradient.lightningCustodianWallet;
|
||||
break;
|
||||
|
|
|
@ -11,7 +11,6 @@ import {
|
|||
HDSegwitP2SHWallet,
|
||||
LegacyWallet,
|
||||
LightningCustodianWallet,
|
||||
LightningLdkWallet,
|
||||
MultisigHDWallet,
|
||||
SLIP39LegacyP2PKHWallet,
|
||||
SLIP39SegwitBech32Wallet,
|
||||
|
@ -183,17 +182,6 @@ const startImport = (
|
|||
yield { wallet: lnd };
|
||||
}
|
||||
|
||||
// is it LDK?
|
||||
yield { progress: 'lightning' };
|
||||
if (text.startsWith('ldk://')) {
|
||||
const ldk = new LightningLdkWallet();
|
||||
ldk.setSecret(text);
|
||||
if (ldk.valid()) {
|
||||
await ldk.init();
|
||||
yield { wallet: ldk };
|
||||
}
|
||||
}
|
||||
|
||||
// check bip39 wallets
|
||||
yield { progress: 'bip39' };
|
||||
const hd2 = new HDSegwitBech32Wallet();
|
||||
|
|
|
@ -1,692 +0,0 @@
|
|||
import RNFS from 'react-native-fs';
|
||||
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
||||
import RnLdk from 'rn-ldk/src/index';
|
||||
import { LightningCustodianWallet } from './lightning-custodian-wallet';
|
||||
import SyncedAsyncStorage from '../synced-async-storage';
|
||||
import { randomBytes } from '../rng';
|
||||
import * as bip39 from 'bip39';
|
||||
import { HDSegwitBech32Wallet } from './hd-segwit-bech32-wallet';
|
||||
import bolt11 from 'bolt11';
|
||||
import { SegwitBech32Wallet } from './segwit-bech32-wallet';
|
||||
import presentAlert from '../../components/Alert';
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
|
||||
export class LightningLdkWallet extends LightningCustodianWallet {
|
||||
static type = 'lightningLdk';
|
||||
static typeReadable = 'Lightning LDK';
|
||||
private _listChannels: any[] = [];
|
||||
private _listPayments: any[] = [];
|
||||
private _listInvoices: any[] = [];
|
||||
private _nodeConnectionDetailsCache: any = {}; // pubkey -> {pubkey, host, port, ts}
|
||||
private _refundAddressScriptHex: string = '';
|
||||
private _lastTimeBlockchainCheckedTs: number = 0;
|
||||
private _unwrapFirstExternalAddressFromMnemonicsCache: string = '';
|
||||
private static _predefinedNodes: Record<string, string> = {
|
||||
Bitrefill: '03d607f3e69fd032524a867b288216bfab263b6eaee4e07783799a6fe69bb84fac@3.237.23.179:9735',
|
||||
'OpenNode.com': '03abf6f44c355dec0d5aa155bdbdd6e0c8fefe318eff402de65c6eb2e1be55dc3e@3.132.230.42:9735',
|
||||
Fold: '02816caed43171d3c9854e3b0ab2cf0c42be086ff1bd4005acc2a5f7db70d83774@35.238.153.25:9735',
|
||||
'Moon (paywithmoon.com)': '025f1456582e70c4c06b61d5c8ed3ce229e6d0db538be337a2dc6d163b0ebc05a5@52.86.210.65:9735',
|
||||
'coingate.com': '0242a4ae0c5bef18048fbecf995094b74bfb0f7391418d71ed394784373f41e4f3@3.124.63.44:9735',
|
||||
'Blockstream Store': '02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f@35.232.170.67:9735',
|
||||
ACINQ: '03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f@3.33.236.230:9735',
|
||||
};
|
||||
|
||||
static getPredefinedNodes() {
|
||||
return LightningLdkWallet._predefinedNodes;
|
||||
}
|
||||
|
||||
static pubkeyToAlias(pubkeyHex: string) {
|
||||
for (const key of Object.keys(LightningLdkWallet._predefinedNodes)) {
|
||||
const val = LightningLdkWallet._predefinedNodes[key];
|
||||
if (val.startsWith(pubkeyHex)) return key;
|
||||
}
|
||||
|
||||
return pubkeyHex;
|
||||
}
|
||||
|
||||
constructor(props?: any) {
|
||||
super(props);
|
||||
this.preferredBalanceUnit = BitcoinUnit.SATS;
|
||||
this.chain = Chain.OFFCHAIN;
|
||||
this.user_invoices_raw = []; // compatibility with other lightning wallet class
|
||||
}
|
||||
|
||||
valid() {
|
||||
try {
|
||||
const entropy = bip39.mnemonicToEntropy(this.secret.replace('ldk://', ''));
|
||||
return entropy.length === 64 || entropy.length === 32;
|
||||
} catch (_) {}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async stop() {
|
||||
return RnLdk.stop();
|
||||
}
|
||||
|
||||
async wipeLndDir() {}
|
||||
|
||||
async listPeers() {
|
||||
return RnLdk.listPeers();
|
||||
}
|
||||
|
||||
async listChannels() {
|
||||
try {
|
||||
// exception might be in case of incompletely-started LDK. then just ignore and return cached version
|
||||
this._listChannels = await RnLdk.listChannels();
|
||||
} catch (_) {}
|
||||
|
||||
return this._listChannels;
|
||||
}
|
||||
|
||||
async getLndTransactions() {
|
||||
return [];
|
||||
}
|
||||
|
||||
async getInfo() {
|
||||
const identityPubkey = await RnLdk.getNodeId();
|
||||
return {
|
||||
identityPubkey,
|
||||
};
|
||||
}
|
||||
|
||||
allowSend() {
|
||||
return true;
|
||||
}
|
||||
|
||||
timeToCheckBlockchain() {
|
||||
return +new Date() - this._lastTimeBlockchainCheckedTs > 5 * 60 * 1000; // 5 min, half of block time
|
||||
}
|
||||
|
||||
async fundingStateStepFinalize(txhex: string) {
|
||||
return RnLdk.openChannelStep2(txhex);
|
||||
}
|
||||
|
||||
async getMaturingBalance(): Promise<number> {
|
||||
return RnLdk.getMaturingBalance();
|
||||
}
|
||||
|
||||
async getMaturingHeight(): Promise<number> {
|
||||
return RnLdk.getMaturingHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Probes getNodeId() call. if its available - LDK has started
|
||||
*
|
||||
* @return {Promise<boolean>}
|
||||
*/
|
||||
async isStarted() {
|
||||
let rez;
|
||||
try {
|
||||
rez = await Promise.race([new Promise(resolve => setTimeout(() => resolve('timeout'), 1000)), RnLdk.getNodeId()]);
|
||||
} catch (_) {}
|
||||
|
||||
if (rez === 'timeout' || !rez) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waiter till getNodeId() starts to respond. Returns true if it eventually does,
|
||||
* false in case of timeout.
|
||||
*
|
||||
* @return {Promise<boolean>}
|
||||
*/
|
||||
async waitTillStarted() {
|
||||
for (let c = 0; c < 30; c++) {
|
||||
if (await this.isStarted()) return true;
|
||||
await new Promise(resolve => setTimeout(resolve, 500)); // sleep
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async openChannel(pubkeyHex: string, host: string, amountSats: number, privateChannel: boolean) {
|
||||
let triedToConnect = false;
|
||||
let port = 9735;
|
||||
|
||||
if (host.includes(':')) {
|
||||
const splitted = host.split(':');
|
||||
host = splitted[0];
|
||||
port = +splitted[1];
|
||||
}
|
||||
|
||||
for (let c = 0; c < 20; c++) {
|
||||
const peers = await this.listPeers();
|
||||
if (peers.includes(pubkeyHex)) {
|
||||
// all good, connected, lets open channel
|
||||
return await RnLdk.openChannelStep1(pubkeyHex, +amountSats);
|
||||
}
|
||||
|
||||
if (!triedToConnect) {
|
||||
triedToConnect = true;
|
||||
await RnLdk.connectPeer(pubkeyHex, host, +port);
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 500)); // sleep
|
||||
}
|
||||
|
||||
throw new Error('timeout waiting for peer connection');
|
||||
}
|
||||
|
||||
async connectPeer(pubkeyHex: string, host: string, port: number) {
|
||||
return RnLdk.connectPeer(pubkeyHex, host, +port);
|
||||
}
|
||||
|
||||
async lookupNodeConnectionDetailsByPubkey(pubkey: string) {
|
||||
// first, trying cache:
|
||||
if (this._nodeConnectionDetailsCache[pubkey] && +new Date() - this._nodeConnectionDetailsCache[pubkey].ts < 4 * 7 * 24 * 3600 * 1000) {
|
||||
// cache hit
|
||||
return this._nodeConnectionDetailsCache[pubkey];
|
||||
}
|
||||
|
||||
// doing actual fetch and filling cache:
|
||||
const response = await fetch(`https://1ml.com/node/${pubkey}/json`);
|
||||
const json = await response.json();
|
||||
if (json && json.addresses && Array.isArray(json.addresses)) {
|
||||
for (const address of json.addresses) {
|
||||
if (address.network === 'tcp') {
|
||||
const ret = {
|
||||
pubkey,
|
||||
host: address.addr.split(':')[0],
|
||||
port: parseInt(address.addr.split(':')[1], 10),
|
||||
};
|
||||
|
||||
this._nodeConnectionDetailsCache[pubkey] = Object.assign({}, ret, { ts: +new Date() });
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAddress() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getSecret() {
|
||||
return this.secret;
|
||||
}
|
||||
|
||||
timeToRefreshBalance() {
|
||||
return (+new Date() - this._lastBalanceFetch) / 1000 > 300; // 5 min
|
||||
}
|
||||
|
||||
timeToRefreshTransaction() {
|
||||
return (+new Date() - this._lastTxFetch) / 1000 > 300; // 5 min
|
||||
}
|
||||
|
||||
async generate() {
|
||||
const buf = await randomBytes(16);
|
||||
this.secret = 'ldk://' + bip39.entropyToMnemonic(buf.toString('hex'));
|
||||
}
|
||||
|
||||
getEntropyHex() {
|
||||
let ret = bip39.mnemonicToEntropy(this.secret.replace('ldk://', ''));
|
||||
while (ret.length < 64) ret = '0' + ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
getStorageNamespace() {
|
||||
return RnLdk.getStorage().namespace;
|
||||
}
|
||||
|
||||
static async _decodeInvoice(invoice: string) {
|
||||
return bolt11.decode(invoice);
|
||||
}
|
||||
|
||||
static async _script2address(scriptHex: string) {
|
||||
return bitcoin.address.fromOutputScript(Buffer.from(scriptHex, 'hex'));
|
||||
}
|
||||
|
||||
async selftest() {
|
||||
await RnLdk.getStorage().selftest();
|
||||
await RnLdk.selftest();
|
||||
}
|
||||
|
||||
async init() {
|
||||
if (!this.getSecret()) return;
|
||||
console.warn('starting ldk');
|
||||
|
||||
try {
|
||||
// providing simple functions that RnLdk would otherwise rely on 3rd party APIs
|
||||
RnLdk.provideDecodeInvoiceFunc(LightningLdkWallet._decodeInvoice);
|
||||
RnLdk.provideScript2addressFunc(LightningLdkWallet._script2address);
|
||||
const syncedStorage = new SyncedAsyncStorage(this.getEntropyHex());
|
||||
// await syncedStorage.selftest();
|
||||
// await RnLdk.selftest();
|
||||
// console.warn('selftest passed');
|
||||
await syncedStorage.synchronize();
|
||||
|
||||
RnLdk.setStorage(syncedStorage);
|
||||
if (this._refundAddressScriptHex) {
|
||||
await RnLdk.setRefundAddressScript(this._refundAddressScriptHex);
|
||||
} else {
|
||||
// fallback, unwrapping address from bip39 mnemonic we have
|
||||
const address = this.unwrapFirstExternalAddressFromMnemonics();
|
||||
await this.setRefundAddress(address);
|
||||
}
|
||||
await RnLdk.start(this.getEntropyHex(), RNFS.DocumentDirectoryPath);
|
||||
|
||||
this._execInBackground(this.reestablishChannels);
|
||||
if (this.timeToCheckBlockchain()) this._execInBackground(this.checkBlockchain);
|
||||
} catch (error: any) {
|
||||
presentAlert({ message: 'LDK init error: ' + error.message });
|
||||
}
|
||||
}
|
||||
|
||||
unwrapFirstExternalAddressFromMnemonics() {
|
||||
if (this._unwrapFirstExternalAddressFromMnemonicsCache) return this._unwrapFirstExternalAddressFromMnemonicsCache; // cache hit
|
||||
const hd = new HDSegwitBech32Wallet();
|
||||
hd.setSecret(this.getSecret().replace('ldk://', ''));
|
||||
const address = hd._getExternalAddressByIndex(0);
|
||||
this._unwrapFirstExternalAddressFromMnemonicsCache = address;
|
||||
return address;
|
||||
}
|
||||
|
||||
unwrapFirstExternalWIFFromMnemonics() {
|
||||
const hd = new HDSegwitBech32Wallet();
|
||||
hd.setSecret(this.getSecret().replace('ldk://', ''));
|
||||
return hd._getExternalWIFByIndex(0);
|
||||
}
|
||||
|
||||
async checkBlockchain() {
|
||||
this._lastTimeBlockchainCheckedTs = +new Date();
|
||||
return RnLdk.checkBlockchain();
|
||||
}
|
||||
|
||||
async payInvoice(invoice: string, freeAmount = 0) {
|
||||
const decoded = this.decodeInvoice(invoice);
|
||||
|
||||
// if its NOT zero amount invoice, we forcefully reset passed amount argument so underlying LDK code
|
||||
// would extract amount from bolt11
|
||||
if (decoded.num_satoshis && parseInt(decoded.num_satoshis, 10) > 0) freeAmount = 0;
|
||||
|
||||
if (await this.channelsNeedReestablish()) {
|
||||
await this.reestablishChannels();
|
||||
await this.waitForAtLeastOneChannelBecomeActive();
|
||||
}
|
||||
|
||||
const result = await RnLdk.payInvoice(invoice, freeAmount);
|
||||
if (!result) throw new Error('Failed');
|
||||
|
||||
// ok, it was sent. now, waiting for an event that it was _actually_ paid:
|
||||
for (let c = 0; c < 60; c++) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500)); // sleep
|
||||
|
||||
for (const sentPayment of RnLdk.sentPayments || []) {
|
||||
const paidHash = LightningLdkWallet.preimage2hash(sentPayment.payment_preimage);
|
||||
if (paidHash === decoded.payment_hash) {
|
||||
this._listPayments = this._listPayments || [];
|
||||
this._listPayments.push(
|
||||
Object.assign({}, sentPayment, {
|
||||
memo: decoded.description || 'Lightning payment',
|
||||
value: (freeAmount || decoded.num_satoshis) * -1,
|
||||
received: +new Date(),
|
||||
payment_preimage: sentPayment.payment_preimage,
|
||||
payment_hash: decoded.payment_hash,
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (const failedPayment of RnLdk.failedPayments || []) {
|
||||
if (failedPayment.payment_hash === decoded.payment_hash) throw new Error(JSON.stringify(failedPayment));
|
||||
}
|
||||
}
|
||||
|
||||
// no? lets just throw timeout error
|
||||
throw new Error('Payment timeout');
|
||||
}
|
||||
|
||||
/**
|
||||
* In case user initiated channel opening, and then lost peer connection (i.e. app went in background for an
|
||||
* extended period of time), when user gets back to the app the channel might already have enough confirmations,
|
||||
* but will never be acknowledged as 'established' by LDK until peer reconnects so that ldk & peer can negotiate and
|
||||
* agree that channel is now established
|
||||
*/
|
||||
async reconnectPeersWithPendingChannels() {
|
||||
const peers = await RnLdk.listPeers();
|
||||
const peers2reconnect: Record<string, boolean> = {};
|
||||
if (this._listChannels) {
|
||||
for (const channel of this._listChannels) {
|
||||
if (!channel.is_funding_locked) {
|
||||
// pending channel
|
||||
if (!peers.includes(channel.remote_node_id)) peers2reconnect[channel.remote_node_id] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const pubkey of Object.keys(peers2reconnect)) {
|
||||
const { host, port } = await this.lookupNodeConnectionDetailsByPubkey(pubkey);
|
||||
await this.connectPeer(pubkey, host, port);
|
||||
}
|
||||
}
|
||||
|
||||
async getUserInvoices(limit = false) {
|
||||
const newInvoices: any[] = [];
|
||||
let found = false;
|
||||
|
||||
// okay, so the idea is that `this._listInvoices` is a persistant storage of invoices, while
|
||||
// `RnLdk.receivedPayments` is only a temp storage of emited events
|
||||
|
||||
// we iterate through all stored invoices
|
||||
for (const invoice of this._listInvoices) {
|
||||
const newInvoice = Object.assign({}, invoice);
|
||||
|
||||
// iterate through events of received payments
|
||||
for (const receivedPayment of RnLdk.receivedPayments || []) {
|
||||
if (receivedPayment.payment_hash === invoice.payment_hash) {
|
||||
// match! this particular payment was paid
|
||||
newInvoice.ispaid = true;
|
||||
newInvoice.value = Math.floor(parseInt(receivedPayment.amt, 10) / 1000);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
newInvoices.push(newInvoice);
|
||||
}
|
||||
|
||||
// overwrite stored array if flag was set
|
||||
if (found) this._listInvoices = newInvoices;
|
||||
|
||||
return this._listInvoices;
|
||||
}
|
||||
|
||||
isInvoiceGeneratedByWallet(paymentRequest: string) {
|
||||
return Boolean(this?._listInvoices?.some(invoice => invoice.payment_request === paymentRequest));
|
||||
}
|
||||
|
||||
weOwnAddress(address: string) {
|
||||
return false;
|
||||
}
|
||||
|
||||
async addInvoice(amtSat: number, memo: string) {
|
||||
if (await this.channelsNeedReestablish()) {
|
||||
await this.reestablishChannels();
|
||||
await this.waitForAtLeastOneChannelBecomeActive();
|
||||
}
|
||||
|
||||
if (this.getReceivableBalance() < amtSat) throw new Error('You dont have enough inbound capacity');
|
||||
|
||||
const paymentRequest = await RnLdk.addInvoice(amtSat * 1000, memo);
|
||||
if (!paymentRequest) return false;
|
||||
|
||||
const decoded = this.decodeInvoice(paymentRequest);
|
||||
|
||||
this._listInvoices = this._listInvoices || [];
|
||||
const tx = {
|
||||
payment_request: paymentRequest,
|
||||
ispaid: false,
|
||||
timestamp: +new Date(),
|
||||
expire_time: 3600 * 1000,
|
||||
amt: amtSat,
|
||||
type: 'user_invoice',
|
||||
payment_hash: decoded.payment_hash,
|
||||
description: memo || '',
|
||||
};
|
||||
this._listInvoices.push(tx);
|
||||
|
||||
return paymentRequest;
|
||||
}
|
||||
|
||||
async getAddressAsync() {
|
||||
throw new Error('getAddressAsync: Not implemented');
|
||||
}
|
||||
|
||||
async allowOnchainAddress(): Promise<boolean> {
|
||||
throw new Error('allowOnchainAddress: Not implemented');
|
||||
}
|
||||
|
||||
getTransactions() {
|
||||
const ret = [];
|
||||
|
||||
for (const payment of this?._listPayments || []) {
|
||||
const newTx = Object.assign({}, payment, {
|
||||
type: 'paid_invoice',
|
||||
walletID: this.getID(),
|
||||
});
|
||||
ret.push(newTx);
|
||||
}
|
||||
|
||||
// ############################################
|
||||
|
||||
for (const invoice of this?._listInvoices || []) {
|
||||
const tx = {
|
||||
payment_request: invoice.payment_request,
|
||||
ispaid: invoice.ispaid,
|
||||
received: invoice.timestamp,
|
||||
type: invoice.type,
|
||||
value: invoice.value || invoice.amt,
|
||||
memo: invoice.description,
|
||||
timestamp: invoice.timestamp, // important
|
||||
expire_time: invoice.expire_time, // important
|
||||
walletID: this.getID(),
|
||||
};
|
||||
|
||||
if (tx.ispaid || invoice.timestamp + invoice.expire_time > +new Date()) {
|
||||
// expired non-paid invoices are not shown
|
||||
ret.push(tx);
|
||||
}
|
||||
}
|
||||
|
||||
ret.sort(function (a, b) {
|
||||
return b.received - a.received;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
async fetchTransactions() {
|
||||
if (this.timeToCheckBlockchain()) {
|
||||
try {
|
||||
// exception might be in case of incompletely-started LDK
|
||||
this._listChannels = await RnLdk.listChannels();
|
||||
await this.checkBlockchain();
|
||||
// ^^^ will be executed if above didnt throw exceptions, which means ldk fully started.
|
||||
// we need this for a case when app returns from background if it was in bg for a really long time.
|
||||
// ldk needs to update it's blockchain data, and this is practically the only place where it can
|
||||
// do that (except on cold start)
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
try {
|
||||
await this.reconnectPeersWithPendingChannels();
|
||||
} catch (error: any) {
|
||||
console.log('fetchTransactions failed');
|
||||
console.log(error.message);
|
||||
}
|
||||
|
||||
await this.getUserInvoices(); // it internally updates paid user invoices
|
||||
}
|
||||
|
||||
getBalance() {
|
||||
let sum = 0;
|
||||
if (this._listChannels) {
|
||||
for (const channel of this._listChannels) {
|
||||
if (!channel.is_funding_locked) continue; // pending channel
|
||||
sum += Math.floor(parseInt(channel.outbound_capacity_msat, 10) / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
getReceivableBalance() {
|
||||
let sum = 0;
|
||||
if (this._listChannels) {
|
||||
for (const channel of this._listChannels) {
|
||||
if (!channel.is_funding_locked) continue; // pending channel
|
||||
sum += Math.floor(parseInt(channel.inbound_capacity_msat, 10) / 1000);
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if there is balance on first unwapped address we have.
|
||||
* This address is a fallback in case user has _no_ other wallets to withdraw onchain coins to, so closed-channel
|
||||
* funds land on this address. Ofcourse, if user provided us a withdraw address, it should be stored in
|
||||
* `this._refundAddressScriptHex` and its balance frankly is not our concern.
|
||||
*
|
||||
* @return {Promise<{confirmedBalance: number}>}
|
||||
*/
|
||||
async walletBalance() {
|
||||
let confirmedSat = 0;
|
||||
if (this._unwrapFirstExternalAddressFromMnemonicsCache) {
|
||||
const response = await fetch('https://blockstream.info/api/address/' + this._unwrapFirstExternalAddressFromMnemonicsCache + '/utxo');
|
||||
const json = await response.json();
|
||||
if (json && Array.isArray(json)) {
|
||||
for (const utxo of json) {
|
||||
if (utxo?.status?.confirmed) {
|
||||
confirmedSat += parseInt(utxo.value, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { confirmedBalance: confirmedSat };
|
||||
}
|
||||
|
||||
async fetchBalance() {
|
||||
await this.listChannels(); // updates channels
|
||||
}
|
||||
|
||||
async claimCoins(address: string) {
|
||||
console.log('unwrapping wif...');
|
||||
const wif = this.unwrapFirstExternalWIFFromMnemonics();
|
||||
const wallet = new SegwitBech32Wallet();
|
||||
wallet.setSecret(String(wif));
|
||||
console.log('fetching balance...');
|
||||
await wallet.fetchUtxo();
|
||||
console.log(wallet.getBalance(), wallet.getUtxo());
|
||||
console.log('creating transation...');
|
||||
// @ts-ignore wtf wallet.getUtxo() and first arg of createTransaction are not compatible
|
||||
const { tx } = wallet.createTransaction(wallet.getUtxo(), [{ address }], 2, address, 0, false, 0);
|
||||
if (!tx) throw new Error('claimCoins: could not create transaction');
|
||||
console.log('broadcasting...');
|
||||
return await wallet.broadcastTx(tx.toHex());
|
||||
}
|
||||
|
||||
async fetchInfo() {
|
||||
throw new Error('fetchInfo: Not implemented');
|
||||
}
|
||||
|
||||
allowReceive() {
|
||||
return true;
|
||||
}
|
||||
|
||||
async closeChannel(fundingTxidHex: string, force = false) {
|
||||
return force ? await RnLdk.closeChannelForce(fundingTxidHex) : await RnLdk.closeChannelCooperatively(fundingTxidHex);
|
||||
}
|
||||
|
||||
getLatestTransactionTime(): string | 0 {
|
||||
if (this.getTransactions().length === 0) {
|
||||
return 0;
|
||||
}
|
||||
let max = -1;
|
||||
for (const tx of this.getTransactions()) {
|
||||
if (tx.received) max = Math.max(tx.received, max);
|
||||
}
|
||||
return new Date(max).toString();
|
||||
}
|
||||
|
||||
async getLogs() {
|
||||
return RnLdk.getLogs()
|
||||
.map(log => log.line)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
async getLogsWithTs() {
|
||||
return RnLdk.getLogs()
|
||||
.map(log => log.ts + ' ' + log.line)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
async fetchPendingTransactions() {}
|
||||
|
||||
async fetchUserInvoices() {
|
||||
await this.getUserInvoices();
|
||||
}
|
||||
|
||||
static preimage2hash(preimageHex: string): string {
|
||||
const hash = bitcoin.crypto.sha256(Buffer.from(preimageHex, 'hex'));
|
||||
return hash.toString('hex');
|
||||
}
|
||||
|
||||
async reestablishChannels() {
|
||||
const connectedInThisRun: any = {};
|
||||
for (const channel of await this.listChannels()) {
|
||||
if (channel.is_usable) continue; // already connected..?
|
||||
if (connectedInThisRun[channel.remote_node_id]) continue; // already tried to reconnect (in case there are several channels with the same node)
|
||||
const { pubkey, host, port } = await this.lookupNodeConnectionDetailsByPubkey(channel.remote_node_id);
|
||||
await this.connectPeer(pubkey, host, port);
|
||||
connectedInThisRun[pubkey] = true;
|
||||
}
|
||||
}
|
||||
|
||||
async channelsNeedReestablish() {
|
||||
const freshListChannels = await this.listChannels();
|
||||
const active = freshListChannels.filter(chan => !!chan.is_usable && chan.is_funding_locked).length;
|
||||
return freshListChannels.length !== +active;
|
||||
}
|
||||
|
||||
async waitForAtLeastOneChannelBecomeActive() {
|
||||
const active = (await this.listChannels()).filter(chan => !!chan.is_usable).length;
|
||||
|
||||
for (let c = 0; c < 10; c++) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500)); // sleep
|
||||
const freshListChannels = await this.listChannels();
|
||||
const active2 = freshListChannels.filter(chan => !!chan.is_usable).length;
|
||||
if (freshListChannels.length === +active2) return true; // all active kek
|
||||
|
||||
if (freshListChannels.length === 0) return true; // no channels at all
|
||||
if (+active2 > +active) return true; // something became active, lets ret
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async setRefundAddress(address: string) {
|
||||
const script = bitcoin.address.toOutputScript(address);
|
||||
this._refundAddressScriptHex = script.toString('hex');
|
||||
await RnLdk.setRefundAddressScript(this._refundAddressScriptHex);
|
||||
}
|
||||
|
||||
static async getVersion() {
|
||||
return RnLdk.getVersion();
|
||||
}
|
||||
|
||||
static getPackageVersion() {
|
||||
return RnLdk.getPackageVersion();
|
||||
}
|
||||
|
||||
getChannelsClosedEvents() {
|
||||
return RnLdk.channelsClosed;
|
||||
}
|
||||
|
||||
async purgeLocalStorage() {
|
||||
return RnLdk.getStorage().purgeLocalStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* executes async function in background, so calling code can return immediately, while catching all thrown exceptions
|
||||
* and showing them in alert() instead of propagating them up
|
||||
*
|
||||
* @param func {function} Async functino to execute
|
||||
* @private
|
||||
*/
|
||||
_execInBackground(func: () => void) {
|
||||
const that = this;
|
||||
(async () => {
|
||||
try {
|
||||
await func.call(that);
|
||||
} catch (error: any) {
|
||||
presentAlert({ message: '_execInBackground error:' + error.message });
|
||||
}
|
||||
})();
|
||||
}
|
||||
}
|
|
@ -9,7 +9,6 @@ import { HDSegwitElectrumSeedP2WPKHWallet } from './hd-segwit-electrum-seed-p2wp
|
|||
import { HDSegwitP2SHWallet } from './hd-segwit-p2sh-wallet';
|
||||
import { LegacyWallet } from './legacy-wallet';
|
||||
import { LightningCustodianWallet } from './lightning-custodian-wallet';
|
||||
import { LightningLdkWallet } from './lightning-ldk-wallet';
|
||||
import { MultisigHDWallet } from './multisig-hd-wallet';
|
||||
import { SegwitBech32Wallet } from './segwit-bech32-wallet';
|
||||
import { SegwitP2SHWallet } from './segwit-p2sh-wallet';
|
||||
|
@ -106,7 +105,6 @@ export type TWallet =
|
|||
| HDSegwitP2SHWallet
|
||||
| LegacyWallet
|
||||
| LightningCustodianWallet
|
||||
| LightningLdkWallet
|
||||
| MultisigHDWallet
|
||||
| SLIP39LegacyP2PKHWallet
|
||||
| SLIP39SegwitBech32Wallet
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef, useContext, useCallback, useMemo }
|
|||
import { Image, Text, TouchableOpacity, View, I18nManager, StyleSheet } from 'react-native';
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import { AbstractWallet, HDSegwitBech32Wallet, LightningCustodianWallet, LightningLdkWallet, MultisigHDWallet } from '../class';
|
||||
import { AbstractWallet, HDSegwitBech32Wallet, LightningCustodianWallet, MultisigHDWallet } from '../class';
|
||||
import { BitcoinUnit } from '../models/bitcoinUnits';
|
||||
import WalletGradient from '../class/wallet-gradient';
|
||||
import Biometric from '../class/biometrics';
|
||||
|
@ -150,7 +150,6 @@ const TransactionsNavigationHeader: React.FC<TransactionsNavigationHeaderProps>
|
|||
<Image
|
||||
source={(() => {
|
||||
switch (wallet.type) {
|
||||
case LightningLdkWallet.type:
|
||||
case LightningCustodianWallet.type:
|
||||
return I18nManager.isRTL ? require('../img/lnd-shape-rtl.png') : require('../img/lnd-shape.png');
|
||||
case MultisigHDWallet.type:
|
||||
|
@ -253,13 +252,6 @@ const TransactionsNavigationHeader: React.FC<TransactionsNavigationHeaderProps>
|
|||
</View>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{wallet.type === LightningLdkWallet.type && (
|
||||
<TouchableOpacity accessibilityRole="button" accessibilityLabel={loc.lnd.title} onPress={handleManageFundsPressed}>
|
||||
<View style={styles.manageFundsButton}>
|
||||
<Text style={styles.manageFundsButtonText}>{loc.lnd.title}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{wallet.type === MultisigHDWallet.type && (
|
||||
<TouchableOpacity accessibilityRole="button" onPress={handleManageFundsPressed}>
|
||||
<View style={styles.manageFundsButton}>
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import loc, { formatBalance, transactionTimeToReadable } from '../loc';
|
||||
import { LightningCustodianWallet, LightningLdkWallet, MultisigHDWallet } from '../class';
|
||||
import { LightningCustodianWallet, MultisigHDWallet } from '../class';
|
||||
import WalletGradient from '../class/wallet-gradient';
|
||||
import { BluePrivateBalance } from '../BlueComponents';
|
||||
import { BlueStorageContext } from '../blue_modules/storage-context';
|
||||
|
@ -155,7 +155,6 @@ export const WalletCarouselItem = ({ item, _, onPress, handleLongPress, isSelect
|
|||
const opacity = isSelectedWallet === false ? 0.5 : 1.0;
|
||||
let image;
|
||||
switch (item.type) {
|
||||
case LightningLdkWallet.type:
|
||||
case LightningCustodianWallet.type:
|
||||
image = I18nManager.isRTL ? require('../img/lnd-shape-rtl.png') : require('../img/lnd-shape.png');
|
||||
break;
|
||||
|
|
|
@ -1,393 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>11</string>
|
||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
<array>
|
||||
<string>io.bluewallet.bluewallet.fetchTxsForWallet</string>
|
||||
</array>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>BlueWallet</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>PSBT</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>io.bluewallet.psbt</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>TXN</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>io.bluewallet.psbt.txn</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>ELECTRUMBACKUP</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>io.bluewallet.backup</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>JSON File</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.json</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>BW COSIGNER</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>io.bluewallet.bwcosigner</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>bitcoin</string>
|
||||
<string>lightning</string>
|
||||
<string>bluewallet</string>
|
||||
<string>lapp</string>
|
||||
<string>blue</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.finance</string>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>https</string>
|
||||
<string>http</string>
|
||||
</array>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>localhost</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>onion</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>tailscale.net</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>ts.net</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>In order to quickly scan the recipient's address, we need your permission to use the camera to scan their QR Code.</string>
|
||||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>In order to use FaceID please confirm your permission.</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>Your authorization is required to save this image.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>In order to import an image for scanning, we need your permission to access your photo library.</string>
|
||||
<key>NSUserActivityTypes</key>
|
||||
<array>
|
||||
<string>io.bluewallet.bluewallet.receiveonchain</string>
|
||||
<string>io.bluewallet.bluewallet.xpub</string>
|
||||
</array>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>AntDesign.ttf</string>
|
||||
<string>Entypo.ttf</string>
|
||||
<string>EvilIcons.ttf</string>
|
||||
<string>Feather.ttf</string>
|
||||
<string>FontAwesome.ttf</string>
|
||||
<string>FontAwesome5_Brands.ttf</string>
|
||||
<string>FontAwesome5_Regular.ttf</string>
|
||||
<string>FontAwesome5_Solid.ttf</string>
|
||||
<string>Foundation.ttf</string>
|
||||
<string>Ionicons.ttf</string>
|
||||
<string>MaterialCommunityIcons.ttf</string>
|
||||
<string>MaterialIcons.ttf</string>
|
||||
<string>Octicons.ttf</string>
|
||||
<string>SimpleLineIcons.ttf</string>
|
||||
<string>Zocial.ttf</string>
|
||||
</array>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>fetch</string>
|
||||
<string>processing</string>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<true/>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Partially Signed Bitcoin Transaction</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.bluewallet.psbt</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>psbt</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.json</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>BW COSIGNER</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.bluewallet.bwcosigner</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>bwcosigner</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Bitcoin Transaction</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.bluewallet.psbt.txn</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>txn</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>UTImportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.text</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>JSON File</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>public.json</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>json</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<array>
|
||||
<string>application/json</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Partially Signed Bitcoin Transaction</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.bluewallet.psbt</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>psbt</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Bitcoin Transaction</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.bluewallet.psbt.txn</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>txn</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Electrum Backup</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.bluewallet.backup</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>backup</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.json</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>BW COSIGNER</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.bluewallet.bwcosigner</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>bwcosigner</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>bugsnag</key>
|
||||
<dict>
|
||||
<key>apiKey</key>
|
||||
<string>17ba9059f676f1cc4f45d98182388b01</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
|
@ -377,7 +377,6 @@
|
|||
B47B21EB2B2128B8001F6690 /* BlueWalletUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlueWalletUITests.swift; sourceTree = "<group>"; };
|
||||
B49038D82B8FBAD300A8164A /* BlueWalletUITest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlueWalletUITest.swift; sourceTree = "<group>"; };
|
||||
B4A29A452B55C990002A67DF /* BlueWallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BlueWallet.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B4A29A462B55C990002A67DF /* BlueWallet-NoLDK.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "BlueWallet-NoLDK.plist"; sourceTree = "<absolute>"; };
|
||||
B4AB21062B61D8CA0080440C /* SplashScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashScreen.swift; sourceTree = "<group>"; };
|
||||
B4AB21082B61DC3F0080440C /* SplashScreen.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SplashScreen.m; sourceTree = "<group>"; };
|
||||
B4AB225C2B02AD12001F4328 /* XMLParserDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLParserDelegate.swift; sourceTree = "<group>"; };
|
||||
|
@ -494,7 +493,6 @@
|
|||
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||
B4A29A462B55C990002A67DF /* BlueWallet-NoLDK.plist */,
|
||||
13B07FB71A68108700A75B9A /* main.m */,
|
||||
32B5A3292334450100F8D608 /* Bridge.swift */,
|
||||
32B5A3282334450100F8D608 /* BlueWallet-Bridging-Header.h */,
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1520"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<PreActions>
|
||||
<ExecutionAction
|
||||
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
||||
<ActionContent
|
||||
title = "Run Script"
|
||||
scriptText = "cd ${SRCROOT} cd .. cp scripts/maccatalystpatches/lightning-ldk-wallet.ts class/wallets/lightning-ldk-wallet.ts ">
|
||||
<EnvironmentBuildable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B4A29A212B55C990002A67DF"
|
||||
BuildableName = "BlueWallet.app"
|
||||
BlueprintName = "BlueWallet-NoLDK"
|
||||
ReferencedContainer = "container:BlueWallet.xcodeproj">
|
||||
</BuildableReference>
|
||||
</EnvironmentBuildable>
|
||||
</ActionContent>
|
||||
</ExecutionAction>
|
||||
</PreActions>
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B4A29A212B55C990002A67DF"
|
||||
BuildableName = "BlueWallet.app"
|
||||
BlueprintName = "BlueWallet-NoLDK"
|
||||
ReferencedContainer = "container:BlueWallet.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Release"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B4A29A212B55C990002A67DF"
|
||||
BuildableName = "BlueWallet.app"
|
||||
BlueprintName = "BlueWallet-NoLDK"
|
||||
ReferencedContainer = "container:BlueWallet.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B4A29A212B55C990002A67DF"
|
||||
BuildableName = "BlueWallet.app"
|
||||
BlueprintName = "BlueWallet-NoLDK"
|
||||
ReferencedContainer = "container:BlueWallet.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -19,11 +19,6 @@
|
|||
<key>orderHint</key>
|
||||
<integer>71</integer>
|
||||
</dict>
|
||||
<key>BlueWallet-NoLDK.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>136</integer>
|
||||
</dict>
|
||||
<key>BlueWallet.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
|
@ -32,12 +27,12 @@
|
|||
<key>BlueWalletUITests.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>97</integer>
|
||||
<integer>8</integer>
|
||||
</dict>
|
||||
<key>BlueWalletWatch (Complication).xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>123</integer>
|
||||
<integer>9</integer>
|
||||
</dict>
|
||||
<key>BlueWalletWatch (Glance).xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
|
@ -107,6 +102,11 @@
|
|||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>B4A29A212B55C990002A67DF</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
{
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "bugsnag-cocoa",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/bugsnag/bugsnag-cocoa",
|
||||
"state" : {
|
||||
"revision" : "49f60b8dc2e94e7ede1114e2c39ba6ac0b576f42",
|
||||
"version" : "6.28.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "efqrcode",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/EFPrefix/EFQRCode.git",
|
||||
"state" : {
|
||||
"revision" : "2991c2f318ad9529d93b2a73a382a3f9c72c64ce",
|
||||
"version" : "6.2.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift_qrcodejs",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/ApolloZhu/swift_qrcodejs.git",
|
||||
"state" : {
|
||||
"revision" : "374dc7f7b9e76c6aeb393f6a84590c6d387e1ecb",
|
||||
"version" : "2.2.2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
|
@ -60,8 +60,6 @@ end
|
|||
|
||||
target 'BlueWallet' do
|
||||
configure_target()
|
||||
# Manually add rn-ldk pod for this target
|
||||
pod 'rn-ldk', :path => '../node_modules/rn-ldk'
|
||||
end
|
||||
|
||||
target 'BlueWallet-NoLDK' do
|
||||
|
|
|
@ -471,8 +471,6 @@ PODS:
|
|||
- React-Core
|
||||
- RealmJS (12.6.0):
|
||||
- React
|
||||
- rn-ldk (0.8.4):
|
||||
- React-Core
|
||||
- RNCAsyncStorage (1.22.3):
|
||||
- React-Core
|
||||
- RNCClipboard (1.13.2):
|
||||
|
@ -585,7 +583,6 @@ DEPENDENCIES:
|
|||
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
|
||||
- ReactNativeCameraKit (from `../node_modules/react-native-camera-kit`)
|
||||
- RealmJS (from `../node_modules/realm`)
|
||||
- rn-ldk (from `../node_modules/rn-ldk`)
|
||||
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
|
||||
- "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)"
|
||||
- "RNCPushNotificationIOS (from `../node_modules/@react-native-community/push-notification-ios`)"
|
||||
|
@ -734,8 +731,6 @@ EXTERNAL SOURCES:
|
|||
:path: "../node_modules/react-native-camera-kit"
|
||||
RealmJS:
|
||||
:path: "../node_modules/realm"
|
||||
rn-ldk:
|
||||
:path: "../node_modules/rn-ldk"
|
||||
RNCAsyncStorage:
|
||||
:path: "../node_modules/@react-native-async-storage/async-storage"
|
||||
RNCClipboard:
|
||||
|
@ -844,7 +839,6 @@ SPEC CHECKSUMS:
|
|||
ReactCommon: cadee954951b13f7550766c0074dd38af2da3575
|
||||
ReactNativeCameraKit: 9d46a5d7dd544ca64aa9c03c150d2348faf437eb
|
||||
RealmJS: a62dc7a1f94b888fe9e8712cd650167ad97dc636
|
||||
rn-ldk: 0d8749d98cc5ce67302a32831818c116b67f7643
|
||||
RNCAsyncStorage: 10591b9e0a91eaffee14e69b3721009759235125
|
||||
RNCClipboard: 60fed4b71560d7bfe40e9d35dea9762b024da86d
|
||||
RNCPushNotificationIOS: 64218f3c776c03d7408284a819b2abfda1834bc8
|
||||
|
@ -869,6 +863,6 @@ SPEC CHECKSUMS:
|
|||
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
|
||||
Yoga: 76b2d5677fc9694bae53c80d0cccfc55719064a3
|
||||
|
||||
PODFILE CHECKSUM: e0fda52578c203cfa975067c3570c97082471d84
|
||||
PODFILE CHECKSUM: 724cdfc1953f7e223f24ab1d579cb8e01c0f1624
|
||||
|
||||
COCOAPODS: 1.14.3
|
||||
|
|
28
package-lock.json
generated
28
package-lock.json
generated
|
@ -12,7 +12,7 @@
|
|||
"dependencies": {
|
||||
"@babel/preset-env": "^7.20.0",
|
||||
"@bugsnag/react-native": "7.22.5",
|
||||
"@bugsnag/source-maps": "2.3.1",
|
||||
"@bugsnag/source-maps": "2.3.2",
|
||||
"@keystonehq/bc-ur-registry": "0.6.4",
|
||||
"@ngraveio/bc-ur": "1.1.6",
|
||||
"@noble/secp256k1": "1.6.3",
|
||||
|
@ -101,7 +101,6 @@
|
|||
"react-native-widget-center": "https://github.com/BlueWallet/react-native-widget-center#a128c38",
|
||||
"readable-stream": "3.6.2",
|
||||
"realm": "12.6.0",
|
||||
"rn-ldk": "github:BlueWallet/rn-ldk#v0.8.4",
|
||||
"rn-nodeify": "10.3.0",
|
||||
"scryptsy": "2.1.0",
|
||||
"slip39": "https://github.com/BlueWallet/slip39-js",
|
||||
|
@ -2284,9 +2283,9 @@
|
|||
"integrity": "sha512-htzFO1Zc57S8kgdRK9mLcPVTW1BY2ijfH7Dk2CeZmspTWKdKqSo1iwmqrq2WtRjFlo8aRZYgLX0wFrDXF/9DLA=="
|
||||
},
|
||||
"node_modules/@bugsnag/source-maps": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@bugsnag/source-maps/-/source-maps-2.3.1.tgz",
|
||||
"integrity": "sha512-9xJTcf5+W7+y1fQBftSOste/3ORi+d5EeCCMdvaHSX69MKQP0lrDiSYpLwX/ErcXrTbvu7nimGGKJP2vBdF7zQ==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@bugsnag/source-maps/-/source-maps-2.3.2.tgz",
|
||||
"integrity": "sha512-FGkGHWzX+W8T5FebBqGTBEPyfVJn4yr0o605mzt0gHVr1OO9i/JeceYZKSCHt116BmuBnwkckUKMH9ABUZ3kaw==",
|
||||
"dependencies": {
|
||||
"command-line-args": "^5.1.1",
|
||||
"command-line-usage": "^6.1.0",
|
||||
|
@ -20598,15 +20597,6 @@
|
|||
"inherits": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/rn-ldk": {
|
||||
"version": "0.8.4",
|
||||
"resolved": "git+ssh://git@github.com/BlueWallet/rn-ldk.git#ab0ce31e4ec8c208fe16954ecbcaba5df60f56c6",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/rn-nodeify": {
|
||||
"version": "10.3.0",
|
||||
"resolved": "https://registry.npmjs.org/rn-nodeify/-/rn-nodeify-10.3.0.tgz",
|
||||
|
@ -24244,9 +24234,9 @@
|
|||
"integrity": "sha512-htzFO1Zc57S8kgdRK9mLcPVTW1BY2ijfH7Dk2CeZmspTWKdKqSo1iwmqrq2WtRjFlo8aRZYgLX0wFrDXF/9DLA=="
|
||||
},
|
||||
"@bugsnag/source-maps": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@bugsnag/source-maps/-/source-maps-2.3.1.tgz",
|
||||
"integrity": "sha512-9xJTcf5+W7+y1fQBftSOste/3ORi+d5EeCCMdvaHSX69MKQP0lrDiSYpLwX/ErcXrTbvu7nimGGKJP2vBdF7zQ==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@bugsnag/source-maps/-/source-maps-2.3.2.tgz",
|
||||
"integrity": "sha512-FGkGHWzX+W8T5FebBqGTBEPyfVJn4yr0o605mzt0gHVr1OO9i/JeceYZKSCHt116BmuBnwkckUKMH9ABUZ3kaw==",
|
||||
"requires": {
|
||||
"command-line-args": "^5.1.1",
|
||||
"command-line-usage": "^6.1.0",
|
||||
|
@ -38033,10 +38023,6 @@
|
|||
"inherits": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"rn-ldk": {
|
||||
"version": "git+ssh://git@github.com/BlueWallet/rn-ldk.git#ab0ce31e4ec8c208fe16954ecbcaba5df60f56c6",
|
||||
"from": "rn-ldk@github:BlueWallet/rn-ldk#v0.8.4"
|
||||
},
|
||||
"rn-nodeify": {
|
||||
"version": "10.3.0",
|
||||
"resolved": "https://registry.npmjs.org/rn-nodeify/-/rn-nodeify-10.3.0.tgz",
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
"android:clean": "cd android; ./gradlew clean ; cd .. ; npm run android",
|
||||
"ios": "react-native run-ios",
|
||||
"postinstall": "rn-nodeify --install buffer,events,process,stream,inherits,path,assert,crypto --hack; npm run releasenotes2json; npm run branch2json; npm run patches",
|
||||
"patches": "patch -p1 < scripts/rn-ldk.patch; patch -p1 < scripts/react-native-camera-kit.patch; patch -p1 < scripts/react-native-widget-center.patch",
|
||||
"patches": "patch -p1 < scripts/react-native-camera-kit.patch; patch -p1 < scripts/react-native-widget-center.patch",
|
||||
"test": "npm run tslint && npm run lint && npm run unit && npm run jest",
|
||||
"jest": "jest -b tests/integration/*",
|
||||
"windowspatches": "./scripts/windows-patches.sh",
|
||||
|
@ -187,7 +187,6 @@
|
|||
"react-native-widget-center": "https://github.com/BlueWallet/react-native-widget-center#a128c38",
|
||||
"readable-stream": "3.6.2",
|
||||
"realm": "12.6.0",
|
||||
"rn-ldk": "github:BlueWallet/rn-ldk#v0.8.4",
|
||||
"rn-nodeify": "10.3.0",
|
||||
"scryptsy": "2.1.0",
|
||||
"slip39": "https://github.com/BlueWallet/slip39-js",
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
// react-native.config.js
|
||||
module.exports = {
|
||||
dependencies: {
|
||||
'rn-ldk': {
|
||||
platforms: {
|
||||
ios: null, // Disable autolinking for ios
|
||||
// android: null, // Uncomment if you also want to disable autolinking for Android
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
|
@ -1,471 +0,0 @@
|
|||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
import { View, StyleSheet, Text, Keyboard, TouchableOpacity, SectionList } from 'react-native';
|
||||
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
|
||||
import { BlueSpacing20, BlueSpacing10, BlueLoading, BlueTextCentered } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import { Chain } from '../../models/bitcoinUnits';
|
||||
import loc, { formatBalance } from '../../loc';
|
||||
import LNNodeBar from '../../components/LNNodeBar';
|
||||
import BottomModal from '../../components/BottomModal';
|
||||
import Button from '../../components/Button';
|
||||
import { Psbt } from 'bitcoinjs-lib';
|
||||
import { AbstractWallet, LightningLdkWallet } from '../../class';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import { useTheme } from '../../components/themes';
|
||||
import StyledButton, { StyledButtonType } from '../../components/StyledButton';
|
||||
import SafeArea from '../../components/SafeArea';
|
||||
const selectWallet = require('../../helpers/select-wallet');
|
||||
const confirm = require('../../helpers/confirm');
|
||||
const LdkNodeInfoChannelStatus = { ACTIVE: 'Active', INACTIVE: 'Inactive', PENDING: 'PENDING', STATUS: 'status' };
|
||||
|
||||
type LdkInfoRouteProps = RouteProp<
|
||||
{
|
||||
params: {
|
||||
walletID: string;
|
||||
psbt: Psbt;
|
||||
};
|
||||
},
|
||||
'params'
|
||||
>;
|
||||
|
||||
const LdkInfo = () => {
|
||||
const { walletID } = useRoute<LdkInfoRouteProps>().params;
|
||||
const { wallets } = useContext(BlueStorageContext);
|
||||
const refreshDataInterval = useRef<NodeJS.Timer>();
|
||||
const sectionList = useRef<SectionList | null>();
|
||||
const wallet: LightningLdkWallet = wallets.find((w: AbstractWallet) => w.getID() === walletID);
|
||||
const { colors } = useTheme();
|
||||
const { setOptions, navigate } = useNavigation();
|
||||
const name = useRoute().name;
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [channels, setChannels] = useState<any[]>([]);
|
||||
const [inactiveChannels, setInactiveChannels] = useState<any[]>([]);
|
||||
const [pendingChannels, setPendingChannels] = useState<any[]>([]);
|
||||
const [wBalance, setWalletBalance] = useState<{ confirmedBalance?: number }>({});
|
||||
const [maturingBalance, setMaturingBalance] = useState(0);
|
||||
const [maturingEta, setMaturingEta] = useState('');
|
||||
const centerContent = channels.length === 0 && pendingChannels.length === 0 && inactiveChannels.length === 0;
|
||||
const allChannelsAmount = useRef(0);
|
||||
// Modals
|
||||
const [selectedChannelIndex, setSelectedChannelIndex] = useState<any>();
|
||||
|
||||
const stylesHook = StyleSheet.create({
|
||||
root: {
|
||||
backgroundColor: colors.background,
|
||||
},
|
||||
listHeaderText: {
|
||||
color: colors.foregroundColor,
|
||||
backgroundColor: colors.background,
|
||||
},
|
||||
listHeaderBack: {
|
||||
backgroundColor: colors.background,
|
||||
},
|
||||
detailsText: {
|
||||
color: colors.alternativeTextColor,
|
||||
},
|
||||
modalContent: {
|
||||
backgroundColor: colors.elevated,
|
||||
},
|
||||
separator: {
|
||||
backgroundColor: colors.inputBorderColor,
|
||||
},
|
||||
});
|
||||
|
||||
const refetchData = async (withLoadingIndicator = true) => {
|
||||
setIsLoading(withLoadingIndicator);
|
||||
|
||||
try {
|
||||
const listChannels = await wallet.listChannels();
|
||||
if (listChannels && Array.isArray(listChannels)) {
|
||||
const activeChannels = listChannels.filter(channel => channel.is_usable === true);
|
||||
setChannels(activeChannels);
|
||||
} else {
|
||||
setChannels([]);
|
||||
}
|
||||
if (listChannels && Array.isArray(listChannels)) {
|
||||
const inactive = listChannels.filter(channel => !channel.is_usable && channel.is_funding_locked);
|
||||
setInactiveChannels(inactive);
|
||||
} else {
|
||||
setInactiveChannels([]);
|
||||
}
|
||||
|
||||
if (listChannels && Array.isArray(listChannels)) {
|
||||
const listPendingChannels = listChannels.filter(channel => !channel.is_funding_locked);
|
||||
setPendingChannels(listPendingChannels);
|
||||
} else {
|
||||
setPendingChannels([]);
|
||||
}
|
||||
const walletBalance: { confirmedBalance?: number } = await wallet.walletBalance();
|
||||
setWalletBalance(walletBalance);
|
||||
|
||||
setMaturingBalance(await wallet.getMaturingBalance());
|
||||
const maturingHeight = await wallet.getMaturingHeight();
|
||||
|
||||
if (maturingHeight > 0) {
|
||||
const result = await fetch('https://blockstream.info/api/blocks/tip/height');
|
||||
const tip = await result.text();
|
||||
const hrs = Math.ceil((maturingHeight - +tip) / 6); // convert blocks to hours
|
||||
setMaturingEta(`${hrs} hours`);
|
||||
} else {
|
||||
setMaturingEta('');
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const channelsAvailable = channels.length + pendingChannels.length + inactiveChannels.length;
|
||||
if (allChannelsAmount.current === 0 && channelsAvailable >= 1) {
|
||||
sectionList?.current?.scrollToLocation({ animated: false, sectionIndex: 0, itemIndex: 0 });
|
||||
}
|
||||
allChannelsAmount.current = channelsAvailable;
|
||||
}, [channels, pendingChannels, inactiveChannels]);
|
||||
|
||||
// do we even need periodic sync when user stares at this screen..?
|
||||
useEffect(() => {
|
||||
refetchData().then(() => {
|
||||
refreshDataInterval.current = setInterval(() => {
|
||||
refetchData(false);
|
||||
if (wallet.timeToCheckBlockchain()) {
|
||||
wallet.checkBlockchain();
|
||||
wallet.reconnectPeersWithPendingChannels();
|
||||
}
|
||||
}, 2000);
|
||||
});
|
||||
return () => {
|
||||
clearInterval(refreshDataInterval?.current as NodeJS.Timeout);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setOptions({
|
||||
headerStyle: {
|
||||
backgroundColor: colors.customHeader,
|
||||
},
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [colors]);
|
||||
|
||||
const showModal = (index: any) => {
|
||||
setSelectedChannelIndex(index);
|
||||
};
|
||||
|
||||
const closeChannel = async (channel: any) => {
|
||||
if (!(await confirm())) return;
|
||||
setSelectedChannelIndex(undefined);
|
||||
|
||||
const wallets2use = wallets.filter((w: AbstractWallet) => w.chain === Chain.ONCHAIN);
|
||||
|
||||
const toWallet: AbstractWallet = await selectWallet(
|
||||
navigate,
|
||||
name,
|
||||
null,
|
||||
wallets2use,
|
||||
'Onchain wallet is required to withdraw funds to',
|
||||
);
|
||||
// using wallets2use instead of simple Chain.ONCHAIN argument because by default this argument only selects wallets
|
||||
// that can send, which is not possible if user wants to withdraw to watch-only wallet
|
||||
if (!toWallet) return;
|
||||
|
||||
console.warn('want to close to wallet ', toWallet.getLabel());
|
||||
const address = await toWallet.getAddressAsync();
|
||||
if (!address) return presentAlert({ message: 'Error: could not get address for channel withdrawal' });
|
||||
await wallet.setRefundAddress(address);
|
||||
|
||||
let forceClose = false;
|
||||
if (!channel.is_usable) {
|
||||
if (!(await confirm(loc.lnd.force_close_channel))) return;
|
||||
forceClose = true;
|
||||
}
|
||||
const rez = await wallet.closeChannel(channel.channel_id, forceClose);
|
||||
if (rez) {
|
||||
presentAlert({ message: loc._.success });
|
||||
return refetchData();
|
||||
}
|
||||
};
|
||||
|
||||
const claimBalance = async () => {
|
||||
const wallets2use = wallets.filter((w: AbstractWallet) => w.chain === Chain.ONCHAIN);
|
||||
const selectedWallet: AbstractWallet = await selectWallet(
|
||||
navigate,
|
||||
name,
|
||||
null,
|
||||
wallets2use,
|
||||
'Onchain wallet is required to withdraw funds to',
|
||||
);
|
||||
// using wallets2use instead of simple Chain.ONCHAIN argument because by default this argument only selects wallets
|
||||
// that can send, which is not possible if user wants to withdraw to watch-only wallet
|
||||
if (!selectedWallet) return;
|
||||
const address = await selectedWallet.getAddressAsync();
|
||||
if (address && (await confirm())) {
|
||||
console.warn('selected ', selectedWallet.getLabel(), address);
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const rez = await wallet.claimCoins(address);
|
||||
if (rez) {
|
||||
presentAlert({ message: loc._.success });
|
||||
await refetchData();
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
Keyboard.dismiss();
|
||||
setSelectedChannelIndex(undefined);
|
||||
};
|
||||
|
||||
const handleOnConnectPeerTapped = async (channelData: any) => {
|
||||
closeModal();
|
||||
const { pubkey, host, port } = await wallet.lookupNodeConnectionDetailsByPubkey(channelData.remote_node_id);
|
||||
return wallet.connectPeer(pubkey, host, port);
|
||||
};
|
||||
|
||||
const renderModal = () => {
|
||||
const status = selectedChannelIndex?.status;
|
||||
const channelData = selectedChannelIndex?.channel.item;
|
||||
return (
|
||||
<BottomModal isVisible={selectedChannelIndex !== undefined} onClose={closeModal} avoidKeyboard>
|
||||
<View style={[styles.modalContent, stylesHook.modalContent]}>
|
||||
<Text style={stylesHook.detailsText}>{loc.lnd.node_alias}</Text>
|
||||
<BlueSpacing10 />
|
||||
{channelData && (
|
||||
<Text style={stylesHook.detailsText}>
|
||||
{LightningLdkWallet.pubkeyToAlias(channelData.remote_node_id) +
|
||||
' (' +
|
||||
channelData.remote_node_id.substr(0, 10) +
|
||||
'...' +
|
||||
channelData.remote_node_id.substr(-6) +
|
||||
')'}
|
||||
</Text>
|
||||
)}
|
||||
<BlueSpacing20 />
|
||||
<LNNodeBar
|
||||
disabled={
|
||||
status === LdkNodeInfoChannelStatus.ACTIVE || status === LdkNodeInfoChannelStatus.INACTIVE ? !channelData?.is_usable : true
|
||||
}
|
||||
canSend={Number(channelData?.outbound_capacity_msat / 1000)}
|
||||
canReceive={Number(channelData?.inbound_capacity_msat / 1000)}
|
||||
itemPriceUnit={wallet.getPreferredBalanceUnit()}
|
||||
/>
|
||||
|
||||
<Text style={stylesHook.detailsText}>
|
||||
{status === LdkNodeInfoChannelStatus.PENDING
|
||||
? loc.transactions.pending
|
||||
: channelData?.is_usable
|
||||
? loc.lnd.active
|
||||
: loc.lnd.inactive}
|
||||
</Text>
|
||||
|
||||
{status === LdkNodeInfoChannelStatus.INACTIVE && (
|
||||
<>
|
||||
<StyledButton
|
||||
onPress={() => handleOnConnectPeerTapped(channelData)}
|
||||
text={loc.lnd.reconnect_peer}
|
||||
buttonStyle={StyledButtonType.grey}
|
||||
/>
|
||||
<BlueSpacing20 />
|
||||
</>
|
||||
)}
|
||||
|
||||
<StyledButton onPress={() => closeChannel(channelData)} text={loc.lnd.close_channel} buttonStyle={StyledButtonType.destroy} />
|
||||
<BlueSpacing20 />
|
||||
</View>
|
||||
</BottomModal>
|
||||
);
|
||||
};
|
||||
|
||||
const renderSectionItem = (item: any) => {
|
||||
switch (item.section.key) {
|
||||
case LdkNodeInfoChannelStatus.ACTIVE:
|
||||
return renderItemChannel({ status: LdkNodeInfoChannelStatus.ACTIVE, channel: item });
|
||||
case LdkNodeInfoChannelStatus.PENDING:
|
||||
return renderItemChannel({ status: LdkNodeInfoChannelStatus.PENDING, channel: item });
|
||||
case LdkNodeInfoChannelStatus.INACTIVE:
|
||||
return renderItemChannel({ status: LdkNodeInfoChannelStatus.INACTIVE, channel: item });
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const renderItemChannel = (channel: any) => {
|
||||
const channelData = channel.channel.item;
|
||||
|
||||
return (
|
||||
<TouchableOpacity accessibilityRole="button" onPress={() => showModal(channel)}>
|
||||
<LNNodeBar
|
||||
disabled={!channelData.is_usable}
|
||||
canSend={Number(channelData.outbound_capacity_msat / 1000)}
|
||||
canReceive={Number(channelData.inbound_capacity_msat / 1000)}
|
||||
itemPriceUnit={wallet.getPreferredBalanceUnit()}
|
||||
nodeAlias={LightningLdkWallet.pubkeyToAlias(channelData.remote_node_id)}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
const navigateToOpenPrivateChannel = async () => {
|
||||
navigateToOpenChannel({ isPrivateChannel: true });
|
||||
};
|
||||
|
||||
const navigateToOpenChannel = async ({ isPrivateChannel }: { isPrivateChannel: boolean }) => {
|
||||
const availableWallets = [...wallets.filter((item: AbstractWallet) => item.isSegwit() && item.allowSend())];
|
||||
if (availableWallets.length === 0) {
|
||||
return presentAlert({ message: loc.lnd.refill_create });
|
||||
}
|
||||
// @ts-ignore: Address types later
|
||||
navigate('LDKOpenChannelRoot', {
|
||||
screen: 'SelectWallet',
|
||||
params: {
|
||||
availableWallets,
|
||||
chainType: Chain.ONCHAIN,
|
||||
onWalletSelect: (selectedWallet: AbstractWallet) => {
|
||||
const selectedWalletID = selectedWallet.getID();
|
||||
selectedWallet.getAddressAsync().then(selectWallet.setRefundAddress);
|
||||
// @ts-ignore: Address types later
|
||||
navigate('LDKOpenChannelRoot', {
|
||||
screen: 'LDKOpenChannelSetAmount',
|
||||
params: {
|
||||
isPrivateChannel,
|
||||
fundingWalletID: selectedWalletID,
|
||||
ldkWalletID: walletID,
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const itemSeparatorComponent = () => {
|
||||
return <View style={[styles.separator, stylesHook.separator]} />;
|
||||
};
|
||||
|
||||
const renderSectionHeader = (section: any) => {
|
||||
switch (section.section.key) {
|
||||
case LdkNodeInfoChannelStatus.PENDING:
|
||||
return <Text style={[styles.listHeaderText, stylesHook.listHeaderText]}>{loc.transactions.pending}</Text>;
|
||||
case LdkNodeInfoChannelStatus.ACTIVE:
|
||||
return <Text style={[styles.listHeaderText, stylesHook.listHeaderText]}>{loc.lnd.active}</Text>;
|
||||
case LdkNodeInfoChannelStatus.INACTIVE:
|
||||
return <Text style={[styles.listHeaderText, stylesHook.listHeaderText]}>{loc.lnd.inactive}</Text>;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const sections = () => {
|
||||
const sectionForList = [];
|
||||
if (channels.length > 0) {
|
||||
sectionForList.push({ key: LdkNodeInfoChannelStatus.ACTIVE, data: channels });
|
||||
}
|
||||
if (inactiveChannels.length > 0) {
|
||||
sectionForList.push({ key: LdkNodeInfoChannelStatus.INACTIVE, data: inactiveChannels });
|
||||
}
|
||||
if (pendingChannels.length > 0) {
|
||||
sectionForList.push({ key: LdkNodeInfoChannelStatus.PENDING, data: pendingChannels });
|
||||
}
|
||||
return sectionForList;
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeArea style={[styles.root, stylesHook.root]}>
|
||||
<SectionList
|
||||
ref={(ref: SectionList) => {
|
||||
sectionList.current = ref;
|
||||
}}
|
||||
renderItem={renderSectionItem}
|
||||
keyExtractor={channel => channel.channel_id}
|
||||
initialNumToRender={7}
|
||||
ItemSeparatorComponent={itemSeparatorComponent}
|
||||
renderSectionHeader={section => (
|
||||
<View style={[styles.listHeaderBack, stylesHook.listHeaderBack]}>{renderSectionHeader(section)}</View>
|
||||
)}
|
||||
contentContainerStyle={[centerContent ? {} : styles.contentContainerStyle, stylesHook.root]}
|
||||
contentInset={{ top: 0, left: 0, bottom: 8, right: 0 }}
|
||||
centerContent={centerContent}
|
||||
sections={sections()}
|
||||
ListEmptyComponent={isLoading ? <BlueLoading /> : <BlueTextCentered>{loc.lnd.no_channels}</BlueTextCentered>}
|
||||
/>
|
||||
{renderModal()}
|
||||
|
||||
<View style={styles.marginHorizontal16}>
|
||||
{wBalance && wBalance.confirmedBalance ? (
|
||||
<>
|
||||
<Button
|
||||
onPress={claimBalance}
|
||||
title={loc.formatString(loc.lnd.claim_balance, {
|
||||
balance: formatBalance(wBalance.confirmedBalance, wallet.getPreferredBalanceUnit()),
|
||||
})}
|
||||
/>
|
||||
<BlueSpacing20 />
|
||||
</>
|
||||
) : null}
|
||||
{maturingBalance ? (
|
||||
<Text style={stylesHook.detailsText}>
|
||||
Balance awaiting confirmations: {formatBalance(Number(maturingBalance), wallet.getPreferredBalanceUnit(), true)}
|
||||
</Text>
|
||||
) : null}
|
||||
{maturingEta ? <Text style={stylesHook.detailsText}>ETA: {maturingEta}</Text> : null}
|
||||
<Button title={loc.lnd.new_channel} onPress={navigateToOpenPrivateChannel} disabled={isLoading} />
|
||||
<BlueSpacing20 />
|
||||
</View>
|
||||
</SafeArea>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
flex: 1,
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
marginHorizontal16: {
|
||||
marginHorizontal: 16,
|
||||
},
|
||||
contentContainerStyle: {
|
||||
marginHorizontal: 16,
|
||||
},
|
||||
listHeaderText: {
|
||||
marginTop: 8,
|
||||
marginBottom: 8,
|
||||
fontWeight: 'bold',
|
||||
fontSize: 24,
|
||||
},
|
||||
listHeaderBack: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
modalContent: {
|
||||
minHeight: 418,
|
||||
borderTopLeftRadius: 16,
|
||||
borderTopRightRadius: 16,
|
||||
borderColor: 'rgba(0, 0, 0, 0.1)',
|
||||
padding: 24,
|
||||
},
|
||||
separator: {
|
||||
height: 1,
|
||||
marginTop: 16,
|
||||
},
|
||||
});
|
||||
|
||||
LdkInfo.navigationOptions = navigationStyle(
|
||||
{
|
||||
title: loc.lnd.channels,
|
||||
},
|
||||
(options, { theme, navigation, route }) => {
|
||||
return {
|
||||
...options,
|
||||
statusBarStyle: 'auto',
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export default LdkInfo;
|
|
@ -1,320 +0,0 @@
|
|||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
|
||||
import { BlueLoading, BlueDismissKeyboardInputAccessory, BlueSpacing20, BlueText } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import AddressInput from '../../components/AddressInput';
|
||||
import AmountInput from '../../components/AmountInput';
|
||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
import loc from '../../loc';
|
||||
import { AbstractWallet, HDSegwitBech32Wallet, LightningLdkWallet } from '../../class';
|
||||
import { ArrowPicker } from '../../components/ArrowPicker';
|
||||
import { Psbt } from 'bitcoinjs-lib';
|
||||
import Biometric from '../../class/biometrics';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import { useTheme } from '../../components/themes';
|
||||
import Button from '../../components/Button';
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
|
||||
import SafeArea from '../../components/SafeArea';
|
||||
import { btcToSatoshi, fiatToBTC } from '../../blue_modules/currency';
|
||||
|
||||
type LdkOpenChannelProps = RouteProp<
|
||||
{
|
||||
params: {
|
||||
isPrivateChannel: boolean;
|
||||
psbt: Psbt;
|
||||
fundingWalletID: string;
|
||||
ldkWalletID: string;
|
||||
remoteHostWithPubkey: string;
|
||||
};
|
||||
},
|
||||
'params'
|
||||
>;
|
||||
|
||||
const LdkOpenChannel = (props: any) => {
|
||||
const { wallets, fetchAndSaveWalletTransactions } = useContext(BlueStorageContext);
|
||||
const [isBiometricUseCapableAndEnabled, setIsBiometricUseCapableAndEnabled] = useState(false);
|
||||
const { colors }: { colors: any } = useTheme();
|
||||
const { navigate, setParams } = useNavigation();
|
||||
const {
|
||||
fundingWalletID,
|
||||
isPrivateChannel,
|
||||
ldkWalletID,
|
||||
psbt,
|
||||
remoteHostWithPubkey = '030c3f19d742ca294a55c00376b3b355c3c90d61c6b6b39554dbc7ac19b141c14f@52.50.244.44:9735' /* Bitrefill */,
|
||||
} = useRoute<LdkOpenChannelProps>().params;
|
||||
const fundingWallet: HDSegwitBech32Wallet = wallets.find((w: AbstractWallet) => w.getID() === fundingWalletID);
|
||||
const ldkWallet: LightningLdkWallet = wallets.find((w: AbstractWallet) => w.getID() === ldkWalletID);
|
||||
const [unit, setUnit] = useState<BitcoinUnit | string>(ldkWallet.getPreferredBalanceUnit());
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const psbtOpenChannelStartedTs = useRef<number>();
|
||||
const name = useRoute().name;
|
||||
const [fundingAmount, setFundingAmount] = useState<any>({ amount: null, amountSats: null });
|
||||
const [verified, setVerified] = useState(false);
|
||||
|
||||
const stylesHook = StyleSheet.create({
|
||||
root: {
|
||||
backgroundColor: colors.elevated,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Handles when user navigates back from transaction creation flow and has PSBT object
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!psbt) return;
|
||||
(async () => {
|
||||
if (psbtOpenChannelStartedTs.current ? +new Date() - psbtOpenChannelStartedTs.current >= 5 * 60 * 1000 : false) {
|
||||
// its 10 min actually, but lets check 5 min just for any case
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
return presentAlert({ message: 'Channel opening expired. Please try again' });
|
||||
}
|
||||
|
||||
setVerified(true);
|
||||
})();
|
||||
}, [psbt]);
|
||||
|
||||
useEffect(() => {
|
||||
Biometric.isBiometricUseCapableAndEnabled().then(setIsBiometricUseCapableAndEnabled);
|
||||
}, []);
|
||||
|
||||
const finalizeOpenChannel = async () => {
|
||||
setIsLoading(true);
|
||||
if (isBiometricUseCapableAndEnabled) {
|
||||
if (!(await Biometric.unlockWithBiometrics())) {
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (psbtOpenChannelStartedTs.current ? +new Date() - psbtOpenChannelStartedTs.current >= 5 * 60 * 1000 : false) {
|
||||
// its 10 min actually, but lets check 5 min just for any case
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
setIsLoading(false);
|
||||
return presentAlert({ message: 'Channel opening expired. Please try again' });
|
||||
}
|
||||
|
||||
const tx = psbt.extractTransaction();
|
||||
const res = await ldkWallet.fundingStateStepFinalize(tx.toHex()); // comment this out to debug
|
||||
// const res = true; // debug
|
||||
if (!res) {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
setIsLoading(false);
|
||||
return presentAlert({ message: 'Something wend wrong during opening channel tx broadcast' });
|
||||
}
|
||||
fetchAndSaveWalletTransactions(ldkWallet.getID());
|
||||
await new Promise(resolve => setTimeout(resolve, 3000)); // sleep to make sure network propagates
|
||||
fetchAndSaveWalletTransactions(fundingWalletID);
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
// @ts-ignore: Address types later
|
||||
navigate('Success', { amount: undefined });
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const openChannel = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const amountSatsNumber = new BigNumber(fundingAmount.amountSats).toNumber();
|
||||
if (!amountSatsNumber) {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
return presentAlert({ message: 'Amount is not valid' });
|
||||
}
|
||||
|
||||
const pubkey = remoteHostWithPubkey.split('@')[0];
|
||||
const host = remoteHostWithPubkey.split('@')[1];
|
||||
if (!pubkey || !host) {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
return presentAlert({ message: 'Remote node address is not valid' });
|
||||
}
|
||||
|
||||
const fundingAddressTemp = await ldkWallet.openChannel(pubkey, host, fundingAmount.amountSats, isPrivateChannel);
|
||||
console.warn('initiated channel opening');
|
||||
|
||||
if (!fundingAddressTemp) {
|
||||
let reason = '';
|
||||
const channelsClosed = ldkWallet.getChannelsClosedEvents();
|
||||
const event = channelsClosed.pop();
|
||||
if (event) {
|
||||
reason += event.reason + ' ' + event.text;
|
||||
}
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
return presentAlert({ message: 'Initiating channel open failed: ' + reason });
|
||||
}
|
||||
|
||||
psbtOpenChannelStartedTs.current = +new Date();
|
||||
// @ts-ignore: Address types later
|
||||
navigate('SendDetailsRoot', {
|
||||
screen: 'SendDetails',
|
||||
params: {
|
||||
memo: 'open channel',
|
||||
address: fundingAddressTemp,
|
||||
walletID: fundingWalletID,
|
||||
amount: fundingAmount.amount,
|
||||
amountSats: fundingAmount.amountSats,
|
||||
unit,
|
||||
noRbf: true,
|
||||
launchedBy: name,
|
||||
isEditable: false,
|
||||
},
|
||||
});
|
||||
} catch (error: any) {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
presentAlert({ message: error.message });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onBarScanned = (ret: { data?: any }) => {
|
||||
if (!ret.data) ret = { data: ret };
|
||||
// @ts-ignore: Address types later
|
||||
setParams({ remoteHostWithPubkey: ret.data });
|
||||
};
|
||||
|
||||
const render = () => {
|
||||
if (isLoading || !ldkWallet || !fundingWallet) {
|
||||
return (
|
||||
<View style={[styles.root, styles.justifyContentCenter, stylesHook.root]}>
|
||||
<BlueLoading style={{}} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
if (verified) {
|
||||
return (
|
||||
<View style={[styles.activeRoot, stylesHook.root]}>
|
||||
<BlueText>
|
||||
{loc.formatString(loc.lnd.opening_channnel_for_from, {
|
||||
forWalletLabel: ldkWallet.getLabel(),
|
||||
fromWalletLabel: fundingWallet.getLabel(),
|
||||
})}
|
||||
</BlueText>
|
||||
|
||||
<BlueText>{loc.lnd.are_you_sure_open_channel}</BlueText>
|
||||
<BlueSpacing20 />
|
||||
<View style={styles.horizontalButtons}>
|
||||
<Button onPress={finalizeOpenChannel} title={loc._.continue} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[styles.activeRoot, stylesHook.root]}>
|
||||
<BlueText>
|
||||
{loc.formatString(loc.lnd.opening_channnel_for_from, {
|
||||
forWalletLabel: ldkWallet.getLabel(),
|
||||
fromWalletLabel: fundingWallet.getLabel(),
|
||||
})}
|
||||
</BlueText>
|
||||
<AmountInput
|
||||
placeholder={loc.lnd.funding_amount_placeholder}
|
||||
isLoading={isLoading}
|
||||
amount={fundingAmount.amount}
|
||||
onAmountUnitChange={(newUnit: string) => {
|
||||
let amountSats = fundingAmount.amountSats;
|
||||
switch (newUnit) {
|
||||
case BitcoinUnit.SATS:
|
||||
amountSats = parseInt(fundingAmount.amount, 10);
|
||||
break;
|
||||
case BitcoinUnit.BTC:
|
||||
amountSats = btcToSatoshi(fundingAmount.amount);
|
||||
break;
|
||||
case BitcoinUnit.LOCAL_CURRENCY:
|
||||
// also accounting for cached fiat->sat conversion to avoid rounding error
|
||||
amountSats = btcToSatoshi(fiatToBTC(fundingAmount.amount));
|
||||
break;
|
||||
}
|
||||
setFundingAmount({ amount: fundingAmount.amount, amountSats });
|
||||
setUnit(newUnit);
|
||||
}}
|
||||
onChangeText={(text: string) => {
|
||||
let amountSats = fundingAmount.amountSats;
|
||||
switch (unit) {
|
||||
case BitcoinUnit.BTC:
|
||||
amountSats = btcToSatoshi(text);
|
||||
break;
|
||||
case BitcoinUnit.LOCAL_CURRENCY:
|
||||
amountSats = btcToSatoshi(fiatToBTC(Number(text)));
|
||||
break;
|
||||
case BitcoinUnit.SATS:
|
||||
amountSats = parseInt(text, 10);
|
||||
break;
|
||||
}
|
||||
setFundingAmount({ amount: text, amountSats });
|
||||
}}
|
||||
unit={unit}
|
||||
inputAccessoryViewID={(BlueDismissKeyboardInputAccessory as any).InputAccessoryViewID}
|
||||
/>
|
||||
|
||||
<AddressInput
|
||||
placeholder={loc.lnd.remote_host}
|
||||
address={remoteHostWithPubkey}
|
||||
isLoading={isLoading}
|
||||
inputAccessoryViewID={(BlueDismissKeyboardInputAccessory as any).InputAccessoryViewID}
|
||||
onChangeText={text =>
|
||||
// @ts-ignore: Address types later
|
||||
setParams({ remoteHostWithPubkey: text })
|
||||
}
|
||||
onBarScanned={onBarScanned}
|
||||
launchedBy={name}
|
||||
/>
|
||||
<BlueDismissKeyboardInputAccessory />
|
||||
|
||||
<ArrowPicker
|
||||
onChange={newKey => {
|
||||
const nodes = LightningLdkWallet.getPredefinedNodes();
|
||||
if (nodes[newKey])
|
||||
// @ts-ignore: Address types later
|
||||
setParams({ remoteHostWithPubkey: nodes[newKey] });
|
||||
}}
|
||||
items={LightningLdkWallet.getPredefinedNodes()}
|
||||
isItemUnknown={!Object.values(LightningLdkWallet.getPredefinedNodes()).some(node => node === remoteHostWithPubkey)}
|
||||
/>
|
||||
<BlueSpacing20 />
|
||||
<View style={styles.horizontalButtons}>
|
||||
<Button onPress={openChannel} disabled={remoteHostWithPubkey.length === 0} title={loc.lnd.open_channel} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
return <SafeArea style={[styles.root, stylesHook.root]}>{render()}</SafeArea>;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
flex: 1,
|
||||
},
|
||||
justifyContentCenter: {
|
||||
justifyContent: 'center',
|
||||
},
|
||||
horizontalButtons: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
activeRoot: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
padding: 16,
|
||||
},
|
||||
});
|
||||
|
||||
LdkOpenChannel.navigationOptions = navigationStyle(
|
||||
{
|
||||
closeButton: true,
|
||||
closeButtonFunc: ({ navigation }) => navigation.getParent().pop(),
|
||||
},
|
||||
(options, { theme, navigation, route }) => {
|
||||
return {
|
||||
...options,
|
||||
headerTitle: loc.lnd.new_channel,
|
||||
headerLargeTitle: true,
|
||||
statusBarStyle: 'auto',
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export default LdkOpenChannel;
|
|
@ -25,17 +25,9 @@ import {
|
|||
} from '../../BlueComponents';
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import {
|
||||
AbstractWallet,
|
||||
HDSegwitBech32Wallet,
|
||||
HDSegwitP2SHWallet,
|
||||
LightningCustodianWallet,
|
||||
LightningLdkWallet,
|
||||
SegwitP2SHWallet,
|
||||
} from '../../class';
|
||||
import { HDSegwitBech32Wallet, HDSegwitP2SHWallet, LightningCustodianWallet, SegwitP2SHWallet } from '../../class';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import Button from '../../components/Button';
|
||||
import { LdkButton } from '../../components/LdkButton';
|
||||
import ListItem from '../../components/ListItem';
|
||||
import { useTheme } from '../../components/themes';
|
||||
import useAsyncPromise from '../../hooks/useAsyncPromise';
|
||||
|
@ -272,35 +264,9 @@ const WalletsAdd: React.FC = () => {
|
|||
setIsLoading(false);
|
||||
// @ts-ignore: Return later to update
|
||||
navigate('WalletsAddMultisig', { walletLabel: label.trim().length > 0 ? label : loc.multisig.default_label });
|
||||
} else if (selectedWalletType === ButtonSelected.LDK) {
|
||||
setIsLoading(false);
|
||||
createLightningLdkWallet();
|
||||
}
|
||||
};
|
||||
|
||||
const createLightningLdkWallet = async () => {
|
||||
const foundLdk = wallets.find((w: AbstractWallet) => w.type === LightningLdkWallet.type);
|
||||
if (foundLdk) {
|
||||
return presentAlert({ message: 'LDK wallet already exists' });
|
||||
}
|
||||
setIsLoading(true);
|
||||
const wallet = new LightningLdkWallet();
|
||||
wallet.setLabel(label || loc.wallets.details_title);
|
||||
|
||||
await wallet.generate();
|
||||
await wallet.init();
|
||||
setIsLoading(false);
|
||||
addWallet(wallet);
|
||||
await saveToDisk();
|
||||
|
||||
A(A.ENUM.CREATED_WALLET);
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
// @ts-ignore: Return later to update
|
||||
navigate('PleaseBackupLdk', {
|
||||
walletID: wallet.getID(),
|
||||
});
|
||||
};
|
||||
|
||||
const createLightningWallet = async () => {
|
||||
const wallet = new LightningCustodianWallet();
|
||||
wallet.setLabel(label || loc.wallets.details_title);
|
||||
|
@ -405,15 +371,6 @@ const WalletsAdd: React.FC = () => {
|
|||
onPress={handleOnLightningButtonPressed}
|
||||
style={styles.button}
|
||||
/>
|
||||
{backdoorPressed > 10 ? (
|
||||
<LdkButton
|
||||
active={selectedWalletType === ButtonSelected.LDK}
|
||||
onPress={handleOnLdkButtonPressed}
|
||||
style={styles.button}
|
||||
subtext={LightningLdkWallet.getPackageVersion()}
|
||||
text="LDK"
|
||||
/>
|
||||
) : null}
|
||||
<VaultButton
|
||||
testID="ActivateVaultButton"
|
||||
active={selectedWalletType === ButtonSelected.VAULT}
|
||||
|
|
|
@ -29,7 +29,6 @@ import {
|
|||
WatchOnlyWallet,
|
||||
MultisigHDWallet,
|
||||
HDAezeedWallet,
|
||||
LightningLdkWallet,
|
||||
} from '../../class';
|
||||
import loc, { formatBalanceWithoutSuffix } from '../../loc';
|
||||
import { useRoute, useNavigation } from '@react-navigation/native';
|
||||
|
@ -176,11 +175,7 @@ const WalletDetails = () => {
|
|||
color: colors.buttonTextColor,
|
||||
},
|
||||
});
|
||||
useEffect(() => {
|
||||
if (wallet.type === LightningLdkWallet.type) {
|
||||
wallet.getInfo().then(setLightningWalletInfo);
|
||||
}
|
||||
}, [wallet]);
|
||||
|
||||
|
||||
const save = () => {
|
||||
setIsLoading(true);
|
||||
|
@ -545,18 +540,6 @@ const WalletDetails = () => {
|
|||
<Text style={[styles.textLabel1, stylesHook.textLabel1]}>{loc.wallets.details_type.toLowerCase()}</Text>
|
||||
<Text style={[styles.textValue, stylesHook.textValue]}>{wallet.typeReadable}</Text>
|
||||
|
||||
{wallet.type === LightningLdkWallet.type && (
|
||||
<>
|
||||
<Text style={[styles.textLabel2, stylesHook.textLabel2]}>{loc.wallets.identity_pubkey}</Text>
|
||||
{lightningWalletInfo?.identityPubkey ? (
|
||||
<>
|
||||
<BlueText>{lightningWalletInfo.identityPubkey}</BlueText>
|
||||
</>
|
||||
) : (
|
||||
<ActivityIndicator />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{wallet.type === MultisigHDWallet.type && (
|
||||
<>
|
||||
<Text style={[styles.textLabel2, stylesHook.textLabel2]}>{loc.wallets.details_multisig_type}</Text>
|
||||
|
@ -694,12 +677,7 @@ const WalletDetails = () => {
|
|||
<SecondButton onPress={navigateToSignVerify} testID="SignVerify" title={loc.addresses.sign_title} />
|
||||
</>
|
||||
)}
|
||||
{wallet.type === LightningLdkWallet.type && (
|
||||
<>
|
||||
<BlueSpacing20 />
|
||||
<SecondButton onPress={navigateToLdkViewLogs} testID="LdkLogs" title={loc.lnd.view_logs} />
|
||||
</>
|
||||
)}
|
||||
|
||||
<BlueSpacing20 />
|
||||
<BlueSpacing20 />
|
||||
<TouchableOpacity accessibilityRole="button" onPress={handleDeleteButtonTapped} testID="DeleteButton">
|
||||
|
|
|
@ -1,160 +0,0 @@
|
|||
import React, { useEffect, useState, useContext, useRef } from 'react';
|
||||
import { StyleSheet, View, ScrollView, TouchableOpacity } from 'react-native';
|
||||
import { useNavigation, useRoute } from '@react-navigation/native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
|
||||
import { BlueLoading, BlueSpacing20, BlueText } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import loc from '../../loc';
|
||||
import { LightningLdkWallet } from '../../class';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import { useTheme } from '../../components/themes';
|
||||
import SafeArea from '../../components/SafeArea';
|
||||
const fs = require('../../blue_modules/fs');
|
||||
|
||||
const LdkViewLogs = () => {
|
||||
const { colors } = useTheme();
|
||||
const { wallets } = useContext(BlueStorageContext);
|
||||
const { walletID } = useRoute().params;
|
||||
/** @type {LightningLdkWallet} */
|
||||
const wallet = wallets.find(w => w.getID() === walletID);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [logs, setLogs] = useState('');
|
||||
const [info, setInfo] = useState('');
|
||||
const [getInfo, setGetInfo] = useState({});
|
||||
const { setOptions } = useNavigation();
|
||||
const refreshDataInterval = useRef();
|
||||
const stylesHooks = StyleSheet.create({
|
||||
root: {
|
||||
backgroundColor: colors.elevated,
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setIsLoading(true);
|
||||
refetchData()
|
||||
.then(() => {
|
||||
refreshDataInterval.current = setInterval(() => {
|
||||
refetchData();
|
||||
}, 5000);
|
||||
})
|
||||
.finally(() => {
|
||||
setOptions({
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
headerRight: () => (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc.wallets.list_tryagain}
|
||||
style={styles.reloadLogs}
|
||||
onPress={getLogs}
|
||||
>
|
||||
<Icon name="redo" type="font-awesome-5" size={22} color={colors.foregroundColor} />
|
||||
</TouchableOpacity>
|
||||
),
|
||||
});
|
||||
});
|
||||
return () => {
|
||||
clearInterval(refreshDataInterval.current);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const getLogs = () => {
|
||||
wallet.getLogs().then(setLogs);
|
||||
};
|
||||
|
||||
const syncBlockchain = () => {
|
||||
wallet.checkBlockchain();
|
||||
};
|
||||
|
||||
const exportLogs = async () => {
|
||||
return fs.writeFileAndExport('rn-ldk.log', info + '\n' + (await wallet.getLogsWithTs()));
|
||||
};
|
||||
|
||||
const selfTest = async () => {
|
||||
try {
|
||||
await wallet.selftest();
|
||||
presentAlert({ message: 'ok' });
|
||||
} catch (error) {
|
||||
presentAlert({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
const refetchData = async () => {
|
||||
getLogs();
|
||||
await wallet
|
||||
.getInfo()
|
||||
.then(async newInfo => {
|
||||
setGetInfo(newInfo);
|
||||
const peers = await wallet.listPeers();
|
||||
const listChannels = await wallet.listChannels();
|
||||
const version = await LightningLdkWallet.getVersion();
|
||||
|
||||
let nfo = 'num peers: ' + peers.length;
|
||||
nfo += '\nnum channels: ' + listChannels.length;
|
||||
nfo += '\nldk binary version: ' + version;
|
||||
nfo += '\nstorage namespace: ' + wallet.getStorageNamespace();
|
||||
setInfo(nfo);
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<View style={[styles.root, stylesHooks.root]}>
|
||||
<BlueLoading />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeArea>
|
||||
<ScrollView style={styles.root}>
|
||||
<TouchableOpacity accessibilityRole="button" onPress={selfTest} style={styles.button}>
|
||||
<BlueText>self test</BlueText>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity accessibilityRole="button" onPress={exportLogs} style={styles.button}>
|
||||
<BlueText>export logs to a file</BlueText>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity accessibilityRole="button" onPress={syncBlockchain} style={styles.button}>
|
||||
<BlueText>sync blockchain</BlueText>
|
||||
</TouchableOpacity>
|
||||
<BlueText>Identity pubkey: {getInfo.identityPubkey}</BlueText>
|
||||
|
||||
<BlueText>{info}</BlueText>
|
||||
<BlueSpacing20 />
|
||||
|
||||
<BlueText>{logs}</BlueText>
|
||||
</ScrollView>
|
||||
</SafeArea>
|
||||
);
|
||||
};
|
||||
|
||||
LdkViewLogs.navigationOptions = navigationStyle({}, opts => ({
|
||||
...opts,
|
||||
title: loc.lnd.view_logs,
|
||||
}));
|
||||
|
||||
export default LdkViewLogs;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
flex: 1,
|
||||
},
|
||||
button: {
|
||||
alignItems: 'center',
|
||||
padding: 10,
|
||||
borderRadius: 15,
|
||||
margin: 5,
|
||||
borderWidth: 1,
|
||||
},
|
||||
reloadLogs: {
|
||||
marginHorizontal: 16,
|
||||
minWidth: 150,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'flex-end',
|
||||
},
|
||||
});
|
|
@ -1,89 +0,0 @@
|
|||
import React, { useCallback, useContext, useEffect } from 'react';
|
||||
import { useNavigation, useRoute } from '@react-navigation/native';
|
||||
import { View, useWindowDimensions, StyleSheet, BackHandler, ScrollView } from 'react-native';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import { BlueCopyTextToClipboard, BlueSpacing20, BlueTextCentered } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import loc from '../../loc';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import { useTheme } from '../../components/themes';
|
||||
import Button from '../../components/Button';
|
||||
import SafeArea from '../../components/SafeArea';
|
||||
import usePrivacy from '../../hooks/usePrivacy';
|
||||
|
||||
const PleaseBackupLdk = () => {
|
||||
const { wallets } = useContext(BlueStorageContext);
|
||||
const { walletID } = useRoute().params;
|
||||
/** @type {LightningLdkWallet} */
|
||||
const wallet = wallets.find(w => w.getID() === walletID);
|
||||
const navigation = useNavigation();
|
||||
const { colors } = useTheme();
|
||||
const { height, width } = useWindowDimensions();
|
||||
const { enableBlur, disableBlur } = usePrivacy();
|
||||
const handleBackButton = useCallback(() => {
|
||||
navigation.getParent().pop();
|
||||
return true;
|
||||
}, [navigation]);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
flex: 1,
|
||||
backgroundColor: colors.elevated,
|
||||
},
|
||||
scrollViewContent: {
|
||||
flexGrow: 1,
|
||||
backgroundColor: colors.elevated,
|
||||
justifyContent: 'center',
|
||||
|
||||
alignItems: 'center',
|
||||
padding: 20,
|
||||
},
|
||||
qrCodeContainer: { borderWidth: 6, borderRadius: 8, borderColor: '#FFFFFF' },
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
enableBlur();
|
||||
BackHandler.addEventListener('hardwareBackPress', handleBackButton);
|
||||
return () => {
|
||||
disableBlur();
|
||||
BackHandler.removeEventListener('hardwareBackPress', handleBackButton);
|
||||
};
|
||||
}, [disableBlur, enableBlur, handleBackButton]);
|
||||
|
||||
const pop = () => navigation.getParent().pop();
|
||||
return (
|
||||
<SafeArea style={styles.root}>
|
||||
<ScrollView centerContent contentContainerStyle={styles.scrollViewContent}>
|
||||
<View>
|
||||
<BlueTextCentered>Please save this wallet backup. It allows you to restore all your channels on other device.</BlueTextCentered>
|
||||
<BlueSpacing20 />
|
||||
</View>
|
||||
<BlueSpacing20 />
|
||||
<View style={styles.qrCodeContainer}>
|
||||
<QRCode
|
||||
value={wallet.secret}
|
||||
logo={require('../../img/qr-code.png')}
|
||||
logoSize={90}
|
||||
size={height > width ? width - 40 : width / 2}
|
||||
color="#000000"
|
||||
logoBackgroundColor={colors.brandingColor}
|
||||
backgroundColor="#FFFFFF"
|
||||
ecl="H"
|
||||
/>
|
||||
</View>
|
||||
<BlueCopyTextToClipboard text={wallet.getSecret()} />
|
||||
<BlueSpacing20 />
|
||||
<Button onPress={pop} title={loc.pleasebackup.ok_lnd} />
|
||||
</ScrollView>
|
||||
</SafeArea>
|
||||
);
|
||||
};
|
||||
|
||||
PleaseBackupLdk.navigationOptions = navigationStyle({
|
||||
title: loc.pleasebackup.title,
|
||||
gestureEnabled: false,
|
||||
swipeEnabled: false,
|
||||
headerBackVisible: false,
|
||||
});
|
||||
|
||||
export default PleaseBackupLdk;
|
|
@ -7,7 +7,7 @@ import { BlueText, BlueSpacing20, BluePrivateBalance } from '../../BlueComponent
|
|||
import navigationStyle from '../../components/navigationStyle';
|
||||
import WalletGradient from '../../class/wallet-gradient';
|
||||
import loc, { formatBalance, transactionTimeToReadable } from '../../loc';
|
||||
import { LightningLdkWallet, MultisigHDWallet, LightningCustodianWallet } from '../../class';
|
||||
import { MultisigHDWallet, LightningCustodianWallet } from '../../class';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import { useTheme } from '../../components/themes';
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
|
||||
|
@ -147,7 +147,6 @@ const SelectWallet = () => {
|
|||
<Image
|
||||
source={(() => {
|
||||
switch (item.type) {
|
||||
case LightningLdkWallet.type:
|
||||
case LightningCustodianWallet.type:
|
||||
return I18nManager.isRTL ? require('../../img/lnd-shape-rtl.png') : require('../../img/lnd-shape.png');
|
||||
case MultisigHDWallet.type:
|
||||
|
|
|
@ -21,7 +21,7 @@ import { Chain } from '../../models/bitcoinUnits';
|
|||
import { BlueAlertWalletExportReminder } from '../../BlueComponents';
|
||||
import WalletGradient from '../../class/wallet-gradient';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { LightningCustodianWallet, LightningLdkWallet, MultisigHDWallet, WatchOnlyWallet } from '../../class';
|
||||
import { LightningCustodianWallet, MultisigHDWallet, WatchOnlyWallet } from '../../class';
|
||||
import ActionSheet from '../ActionSheet';
|
||||
import loc from '../../loc';
|
||||
import { FContainer, FButton } from '../../components/FloatButtons';
|
||||
|
@ -183,12 +183,6 @@ const WalletTransactions = ({ navigation }) => {
|
|||
return false;
|
||||
};
|
||||
|
||||
const refreshLnNodeInfo = () => {
|
||||
if (wallet.type === LightningLdkWallet.type) {
|
||||
setLnNodeInfo({ canReceive: wallet.getReceivableBalance(), canSend: wallet.getBalance() });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Forcefully fetches TXs and balance for wallet
|
||||
*/
|
||||
|
@ -199,7 +193,6 @@ const WalletTransactions = ({ navigation }) => {
|
|||
let noErr = true;
|
||||
let smthChanged = false;
|
||||
try {
|
||||
refreshLnNodeInfo();
|
||||
// await BlueElectrum.ping();
|
||||
await BlueElectrum.waitTillConnected();
|
||||
if (wallet.allowBIP47() && wallet.isBIP47Enabled()) {
|
||||
|
@ -275,11 +268,7 @@ const WalletTransactions = ({ navigation }) => {
|
|||
return (
|
||||
<View style={styles.flex}>
|
||||
<View style={styles.listHeader}>{wallet.chain === Chain.OFFCHAIN && renderLappBrowserButton()}</View>
|
||||
{wallet.type === LightningLdkWallet.type && (lnNodeInfo.canSend > 0 || lnNodeInfo.canReceive > 0) && (
|
||||
<View style={[styles.marginHorizontal18, styles.marginBottom18]}>
|
||||
<LNNodeBar canSend={lnNodeInfo.canSend} canReceive={lnNodeInfo.canReceive} itemPriceUnit={itemPriceUnit} />
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View style={styles.listHeaderTextRow}>
|
||||
<Text style={[styles.listHeaderText, stylesHook.listHeaderText]}>{loc.transactions.list_title}</Text>
|
||||
</View>
|
||||
|
@ -538,8 +527,6 @@ const WalletTransactions = ({ navigation }) => {
|
|||
onManageFundsPressed={id => {
|
||||
if (wallet.type === MultisigHDWallet.type) {
|
||||
navigateToViewEditCosigners();
|
||||
} else if (wallet.type === LightningLdkWallet.type) {
|
||||
navigate('LdkInfo', { walletID: wallet.getID() });
|
||||
} else if (wallet.type === LightningCustodianWallet.type) {
|
||||
if (wallet.getUserHasSavedExport()) {
|
||||
onManageFundsPressed({ id });
|
||||
|
|
|
@ -1,469 +0,0 @@
|
|||
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
||||
import { LightningCustodianWallet } from './lightning-custodian-wallet';
|
||||
import { randomBytes } from '../rng';
|
||||
import * as bip39 from 'bip39';
|
||||
import { HDSegwitBech32Wallet } from './hd-segwit-bech32-wallet';
|
||||
import bolt11 from 'bolt11';
|
||||
import { SegwitBech32Wallet } from './segwit-bech32-wallet';
|
||||
import presentAlert from '../../components/Alert';
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
|
||||
export class LightningLdkWallet extends LightningCustodianWallet {
|
||||
static type = 'lightningLdk';
|
||||
static typeReadable = 'Lightning LDK';
|
||||
private _listChannels: any[] = [];
|
||||
private _listPayments: any[] = [];
|
||||
private _listInvoices: any[] = [];
|
||||
private _nodeConnectionDetailsCache: any = {}; // pubkey -> {pubkey, host, port, ts}
|
||||
private _refundAddressScriptHex: string = '';
|
||||
private _lastTimeBlockchainCheckedTs: number = 0;
|
||||
private _unwrapFirstExternalAddressFromMnemonicsCache: string = '';
|
||||
private static _predefinedNodes: Record<string, string> = {
|
||||
Bitrefill: '03d607f3e69fd032524a867b288216bfab263b6eaee4e07783799a6fe69bb84fac@3.237.23.179:9735',
|
||||
'OpenNode.com': '03abf6f44c355dec0d5aa155bdbdd6e0c8fefe318eff402de65c6eb2e1be55dc3e@3.132.230.42:9735',
|
||||
Fold: '02816caed43171d3c9854e3b0ab2cf0c42be086ff1bd4005acc2a5f7db70d83774@35.238.153.25:9735',
|
||||
'Moon (paywithmoon.com)': '025f1456582e70c4c06b61d5c8ed3ce229e6d0db538be337a2dc6d163b0ebc05a5@52.86.210.65:9735',
|
||||
'coingate.com': '0242a4ae0c5bef18048fbecf995094b74bfb0f7391418d71ed394784373f41e4f3@3.124.63.44:9735',
|
||||
'Blockstream Store': '02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f@35.232.170.67:9735',
|
||||
ACINQ: '03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f@3.33.236.230:9735',
|
||||
};
|
||||
|
||||
static getPredefinedNodes() {
|
||||
return LightningLdkWallet._predefinedNodes;
|
||||
}
|
||||
|
||||
static pubkeyToAlias(pubkeyHex: string) {
|
||||
for (const key of Object.keys(LightningLdkWallet._predefinedNodes)) {
|
||||
const val = LightningLdkWallet._predefinedNodes[key];
|
||||
if (val.startsWith(pubkeyHex)) return key;
|
||||
}
|
||||
|
||||
return pubkeyHex;
|
||||
}
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.preferredBalanceUnit = BitcoinUnit.SATS;
|
||||
this.chain = Chain.OFFCHAIN;
|
||||
this.user_invoices_raw = []; // compatibility with other lightning wallet class
|
||||
}
|
||||
|
||||
valid() {
|
||||
try {
|
||||
const entropy = bip39.mnemonicToEntropy(this.secret.replace('ldk://', ''));
|
||||
return entropy.length === 64 || entropy.length === 32;
|
||||
} catch (_) {}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async stop() {}
|
||||
|
||||
async wipeLndDir() {}
|
||||
|
||||
async listPeers() {}
|
||||
|
||||
async listChannels() {
|
||||
try {
|
||||
// exception might be in case of incompletely-started LDK. then just ignore and return cached version
|
||||
this._listChannels = [];
|
||||
} catch (_) {}
|
||||
|
||||
return this._listChannels;
|
||||
}
|
||||
|
||||
async getLndTransactions() {
|
||||
return [];
|
||||
}
|
||||
|
||||
async getInfo() {
|
||||
const identityPubkey = '';
|
||||
return {
|
||||
identityPubkey,
|
||||
};
|
||||
}
|
||||
|
||||
allowSend() {
|
||||
return true;
|
||||
}
|
||||
|
||||
timeToCheckBlockchain() {
|
||||
return +new Date() - this._lastTimeBlockchainCheckedTs > 5 * 60 * 1000; // 5 min, half of block time
|
||||
}
|
||||
|
||||
async fundingStateStepFinalize(txhex: string) {
|
||||
return false;
|
||||
}
|
||||
|
||||
async getMaturingBalance(): Promise<number> {
|
||||
return 0;
|
||||
}
|
||||
|
||||
async getMaturingHeight(): Promise<number> {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Probes getNodeId() call. if its available - LDK has started
|
||||
*
|
||||
* @return {Promise<boolean>}
|
||||
*/
|
||||
async isStarted() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waiter till getNodeId() starts to respond. Returns true if it eventually does,
|
||||
* false in case of timeout.
|
||||
*
|
||||
* @return {Promise<boolean>}
|
||||
*/
|
||||
async waitTillStarted() {
|
||||
for (let c = 0; c < 30; c++) {
|
||||
if (await this.isStarted()) return true;
|
||||
await new Promise(resolve => setTimeout(resolve, 500)); // sleep
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async openChannel(pubkeyHex: string, host: string, amountSats: number, privateChannel: boolean) {
|
||||
return false;
|
||||
}
|
||||
|
||||
async connectPeer(pubkeyHex: string, host: string, port: number) {}
|
||||
|
||||
async lookupNodeConnectionDetailsByPubkey(pubkey: string) {
|
||||
// first, trying cache:
|
||||
if (this._nodeConnectionDetailsCache[pubkey] && +new Date() - this._nodeConnectionDetailsCache[pubkey].ts < 4 * 7 * 24 * 3600 * 1000) {
|
||||
// cache hit
|
||||
return this._nodeConnectionDetailsCache[pubkey];
|
||||
}
|
||||
|
||||
// doing actual fetch and filling cache:
|
||||
const response = await fetch(`https://1ml.com/node/${pubkey}/json`);
|
||||
const json = await response.json();
|
||||
if (json && json.addresses && Array.isArray(json.addresses)) {
|
||||
for (const address of json.addresses) {
|
||||
if (address.network === 'tcp') {
|
||||
const ret = {
|
||||
pubkey,
|
||||
host: address.addr.split(':')[0],
|
||||
port: parseInt(address.addr.split(':')[1]),
|
||||
};
|
||||
|
||||
this._nodeConnectionDetailsCache[pubkey] = Object.assign({}, ret, { ts: +new Date() });
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAddress() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getSecret() {
|
||||
return this.secret;
|
||||
}
|
||||
|
||||
timeToRefreshBalance() {
|
||||
return (+new Date() - this._lastBalanceFetch) / 1000 > 300; // 5 min
|
||||
}
|
||||
|
||||
timeToRefreshTransaction() {
|
||||
return (+new Date() - this._lastTxFetch) / 1000 > 300; // 5 min
|
||||
}
|
||||
|
||||
async generate() {
|
||||
const buf = await randomBytes(16);
|
||||
this.secret = 'ldk://' + bip39.entropyToMnemonic(buf.toString('hex'));
|
||||
}
|
||||
|
||||
getEntropyHex() {
|
||||
let ret = bip39.mnemonicToEntropy(this.secret.replace('ldk://', ''));
|
||||
while (ret.length < 64) ret = '0' + ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
getStorageNamespace() {}
|
||||
|
||||
static async _decodeInvoice(invoice: string) {
|
||||
return bolt11.decode(invoice);
|
||||
}
|
||||
|
||||
static async _script2address(scriptHex: string) {
|
||||
return bitcoin.address.fromOutputScript(Buffer.from(scriptHex, 'hex'));
|
||||
}
|
||||
|
||||
async selftest() {}
|
||||
|
||||
async init() {}
|
||||
|
||||
unwrapFirstExternalAddressFromMnemonics() {
|
||||
if (this._unwrapFirstExternalAddressFromMnemonicsCache) return this._unwrapFirstExternalAddressFromMnemonicsCache; // cache hit
|
||||
const hd = new HDSegwitBech32Wallet();
|
||||
hd.setSecret(this.getSecret().replace('ldk://', ''));
|
||||
const address = hd._getExternalAddressByIndex(0);
|
||||
this._unwrapFirstExternalAddressFromMnemonicsCache = address;
|
||||
return address;
|
||||
}
|
||||
|
||||
unwrapFirstExternalWIFFromMnemonics() {
|
||||
const hd = new HDSegwitBech32Wallet();
|
||||
hd.setSecret(this.getSecret().replace('ldk://', ''));
|
||||
return hd._getExternalWIFByIndex(0);
|
||||
}
|
||||
|
||||
async checkBlockchain() {}
|
||||
|
||||
async payInvoice(invoice: string, freeAmount = 0) {
|
||||
// no? lets just throw timeout error
|
||||
throw new Error('Payment timeout');
|
||||
}
|
||||
|
||||
/**
|
||||
* In case user initiated channel opening, and then lost peer connection (i.e. app went in background for an
|
||||
* extended period of time), when user gets back to the app the channel might already have enough confirmations,
|
||||
* but will never be acknowledged as 'established' by LDK until peer reconnects so that ldk & peer can negotiate and
|
||||
* agree that channel is now established
|
||||
*/
|
||||
async reconnectPeersWithPendingChannels() {}
|
||||
|
||||
async getUserInvoices(limit = false) {
|
||||
const newInvoices: any[] = [];
|
||||
|
||||
return newInvoices;
|
||||
}
|
||||
|
||||
isInvoiceGeneratedByWallet(paymentRequest: string) {
|
||||
return Boolean(this?._listInvoices?.some(invoice => invoice.payment_request === paymentRequest));
|
||||
}
|
||||
|
||||
weOwnAddress(address: string) {
|
||||
return false;
|
||||
}
|
||||
|
||||
async addInvoice(amtSat: number, memo: string) {}
|
||||
|
||||
async getAddressAsync() {
|
||||
throw new Error('getAddressAsync: Not implemented');
|
||||
}
|
||||
|
||||
async allowOnchainAddress(): Promise<boolean> {
|
||||
throw new Error('allowOnchainAddress: Not implemented');
|
||||
}
|
||||
|
||||
getTransactions() {
|
||||
const ret = [];
|
||||
|
||||
for (const payment of this?._listPayments || []) {
|
||||
const newTx = Object.assign({}, payment, {
|
||||
type: 'paid_invoice',
|
||||
walletID: this.getID(),
|
||||
});
|
||||
ret.push(newTx);
|
||||
}
|
||||
|
||||
// ############################################
|
||||
|
||||
for (const invoice of this?._listInvoices || []) {
|
||||
const tx = {
|
||||
payment_request: invoice.payment_request,
|
||||
ispaid: invoice.ispaid,
|
||||
received: invoice.timestamp,
|
||||
type: invoice.type,
|
||||
value: invoice.value || invoice.amt,
|
||||
memo: invoice.description,
|
||||
timestamp: invoice.timestamp, // important
|
||||
expire_time: invoice.expire_time, // important
|
||||
walletID: this.getID(),
|
||||
};
|
||||
|
||||
if (tx.ispaid || invoice.timestamp + invoice.expire_time > +new Date()) {
|
||||
// expired non-paid invoices are not shown
|
||||
ret.push(tx);
|
||||
}
|
||||
}
|
||||
|
||||
ret.sort(function (a, b) {
|
||||
return b.received - a.received;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
async fetchTransactions() {}
|
||||
|
||||
getBalance() {
|
||||
let sum = 0;
|
||||
if (this._listChannels) {
|
||||
for (const channel of this._listChannels) {
|
||||
if (!channel.is_funding_locked) continue; // pending channel
|
||||
sum += Math.floor(parseInt(channel.outbound_capacity_msat) / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
getReceivableBalance() {
|
||||
let sum = 0;
|
||||
if (this._listChannels) {
|
||||
for (const channel of this._listChannels) {
|
||||
if (!channel.is_funding_locked) continue; // pending channel
|
||||
sum += Math.floor(parseInt(channel.inbound_capacity_msat) / 1000);
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if there is balance on first unwapped address we have.
|
||||
* This address is a fallback in case user has _no_ other wallets to withdraw onchain coins to, so closed-channel
|
||||
* funds land on this address. Ofcourse, if user provided us a withdraw address, it should be stored in
|
||||
* `this._refundAddressScriptHex` and its balance frankly is not our concern.
|
||||
*
|
||||
* @return {Promise<{confirmedBalance: number}>}
|
||||
*/
|
||||
async walletBalance() {
|
||||
let confirmedSat = 0;
|
||||
if (this._unwrapFirstExternalAddressFromMnemonicsCache) {
|
||||
const response = await fetch('https://blockstream.info/api/address/' + this._unwrapFirstExternalAddressFromMnemonicsCache + '/utxo');
|
||||
const json = await response.json();
|
||||
if (json && Array.isArray(json)) {
|
||||
for (const utxo of json) {
|
||||
if (utxo?.status?.confirmed) {
|
||||
confirmedSat += parseInt(utxo.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { confirmedBalance: confirmedSat };
|
||||
}
|
||||
|
||||
async fetchBalance() {
|
||||
await this.listChannels(); // updates channels
|
||||
}
|
||||
|
||||
async claimCoins(address: string) {
|
||||
console.log('unwrapping wif...');
|
||||
const wif = this.unwrapFirstExternalWIFFromMnemonics();
|
||||
const wallet = new SegwitBech32Wallet();
|
||||
wallet.setSecret(String(wif));
|
||||
console.log('fetching balance...');
|
||||
await wallet.fetchUtxo();
|
||||
console.log(wallet.getBalance(), wallet.getUtxo());
|
||||
console.log('creating transation...');
|
||||
const { tx } = wallet.createTransaction(wallet.getUtxo(), [{ address }], 2, address, 0, false, 0);
|
||||
if (!tx) throw new Error('claimCoins: could not create transaction');
|
||||
console.log('broadcasting...');
|
||||
return await wallet.broadcastTx(tx.toHex());
|
||||
}
|
||||
|
||||
async fetchInfo() {
|
||||
throw new Error('fetchInfo: Not implemented');
|
||||
}
|
||||
|
||||
allowReceive() {
|
||||
return true;
|
||||
}
|
||||
|
||||
async closeChannel(fundingTxidHex: string, force = false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
getLatestTransactionTime(): string | 0 {
|
||||
if (this.getTransactions().length === 0) {
|
||||
return 0;
|
||||
}
|
||||
let max = -1;
|
||||
for (const tx of this.getTransactions()) {
|
||||
if (tx.received) max = Math.max(tx.received, max);
|
||||
}
|
||||
return new Date(max).toString();
|
||||
}
|
||||
|
||||
async getLogs() {}
|
||||
|
||||
async getLogsWithTs() {}
|
||||
|
||||
async fetchPendingTransactions() {}
|
||||
|
||||
async fetchUserInvoices() {
|
||||
await this.getUserInvoices();
|
||||
}
|
||||
|
||||
static preimage2hash(preimageHex: string): string {
|
||||
const hash = bitcoin.crypto.sha256(Buffer.from(preimageHex, 'hex'));
|
||||
return hash.toString('hex');
|
||||
}
|
||||
|
||||
async reestablishChannels() {
|
||||
const connectedInThisRun: any = {};
|
||||
for (const channel of await this.listChannels()) {
|
||||
if (channel.is_usable) continue; // already connected..?
|
||||
if (connectedInThisRun[channel.remote_node_id]) continue; // already tried to reconnect (in case there are several channels with the same node)
|
||||
const { pubkey, host, port } = await this.lookupNodeConnectionDetailsByPubkey(channel.remote_node_id);
|
||||
await this.connectPeer(pubkey, host, port);
|
||||
connectedInThisRun[pubkey] = true;
|
||||
}
|
||||
}
|
||||
|
||||
async channelsNeedReestablish() {
|
||||
const freshListChannels = await this.listChannels();
|
||||
const active = freshListChannels.filter(chan => !!chan.is_usable && chan.is_funding_locked).length;
|
||||
return freshListChannels.length !== +active;
|
||||
}
|
||||
|
||||
async waitForAtLeastOneChannelBecomeActive() {
|
||||
const active = (await this.listChannels()).filter(chan => !!chan.is_usable).length;
|
||||
|
||||
for (let c = 0; c < 10; c++) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500)); // sleep
|
||||
const freshListChannels = await this.listChannels();
|
||||
const active2 = freshListChannels.filter(chan => !!chan.is_usable).length;
|
||||
if (freshListChannels.length === +active2) return true; // all active kek
|
||||
|
||||
if (freshListChannels.length === 0) return true; // no channels at all
|
||||
if (+active2 > +active) return true; // something became active, lets ret
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async setRefundAddress(address: string) {
|
||||
const script = bitcoin.address.toOutputScript(address);
|
||||
this._refundAddressScriptHex = script.toString('hex');
|
||||
}
|
||||
|
||||
static async getVersion() {}
|
||||
|
||||
static getPackageVersion() {}
|
||||
|
||||
getChannelsClosedEvents() {
|
||||
return [{ reason: '', text: '' }];
|
||||
}
|
||||
|
||||
async purgeLocalStorage() {}
|
||||
|
||||
/**
|
||||
* executes async function in background, so calling code can return immediately, while catching all thrown exceptions
|
||||
* and showing them in alert() instead of propagating them up
|
||||
*
|
||||
* @param func {function} Async functino to execute
|
||||
* @private
|
||||
*/
|
||||
_execInBackground(func: () => void) {
|
||||
const that = this;
|
||||
(async () => {
|
||||
try {
|
||||
await func.call(that);
|
||||
} catch (error: any) {
|
||||
presentAlert({ message: '_execInBackground error:' + error.message});
|
||||
}
|
||||
})();
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
import { LightningLdkWallet } from '../../class';
|
||||
import SyncedAsyncStorage from '../../class/synced-async-storage';
|
||||
const assert = require('assert');
|
||||
|
||||
describe('', () => {
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('can import & dump LDK bytes from network storage ', async () => {
|
||||
const ldk = new LightningLdkWallet();
|
||||
ldk.setSecret('');
|
||||
assert.ok(ldk.valid());
|
||||
|
||||
const syncedStorage = new SyncedAsyncStorage(ldk.getEntropyHex());
|
||||
const keys = await syncedStorage.getAllKeysRemote();
|
||||
for (const k of keys) {
|
||||
let val = await syncedStorage.getItemRemote(k);
|
||||
val = syncedStorage.decrypt(val);
|
||||
console.log(`${k}\n---------------------------------------\n${val}`);
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,63 +0,0 @@
|
|||
import { HDSegwitBech32Wallet, LightningLdkWallet } from '../../class';
|
||||
const assert = require('assert');
|
||||
|
||||
describe('', () => {
|
||||
function isHex(h) {
|
||||
const re = /[0-9A-Fa-f]{6}/g;
|
||||
return re.test(h);
|
||||
}
|
||||
|
||||
it('can generate', async () => {
|
||||
const ldk = new LightningLdkWallet();
|
||||
await ldk.generate();
|
||||
const secret = ldk.getSecret();
|
||||
assert.ok(secret.startsWith('ldk://'), 'got ' + secret);
|
||||
assert.ok(ldk.valid());
|
||||
assert.ok(isHex(ldk.getEntropyHex()));
|
||||
assert.ok(!isHex(ldk.getSecret()));
|
||||
assert.strictEqual(ldk.getEntropyHex().length, 64);
|
||||
assert.strictEqual(ldk.getEntropyHex(), ldk.getEntropyHex().toLowerCase());
|
||||
|
||||
//
|
||||
|
||||
const hd2 = new HDSegwitBech32Wallet();
|
||||
hd2.setSecret(ldk.getSecret().replace('ldk://', ''));
|
||||
assert.ok(hd2.validateMnemonic());
|
||||
|
||||
//
|
||||
|
||||
ldk.setSecret(secret);
|
||||
assert.ok(ldk.valid());
|
||||
ldk.setSecret('bla');
|
||||
assert.ok(!ldk.valid());
|
||||
|
||||
//
|
||||
|
||||
ldk.setSecret('ldk://zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote');
|
||||
assert.strictEqual(ldk.getEntropyHex(), 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
||||
});
|
||||
|
||||
it('can convert preimage to hash', () => {
|
||||
assert.strictEqual(
|
||||
LightningLdkWallet.preimage2hash('7ec674e1edfc6f5cf32b445e12aa1a1fe0c91b97dd61f98bb41214f31f3642d0'),
|
||||
'294c32ef715c92ac72af888b735950e8a8ea51c00bd4a01572a8da772956dde5',
|
||||
);
|
||||
});
|
||||
|
||||
it('can work with 12 words mnemonics instead of 24', () => {
|
||||
const ldk = new LightningLdkWallet();
|
||||
ldk.setSecret('ldk://abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about');
|
||||
assert.strictEqual(ldk.getEntropyHex(), '0000000000000000000000000000000000000000000000000000000000000000');
|
||||
assert.ok(ldk.valid());
|
||||
assert.ok(isHex(ldk.getEntropyHex()));
|
||||
assert.ok(!isHex(ldk.getSecret()));
|
||||
assert.strictEqual(ldk.getEntropyHex().length, 64);
|
||||
assert.strictEqual(ldk.getEntropyHex(), ldk.getEntropyHex().toLowerCase());
|
||||
});
|
||||
|
||||
it('can unwrap address', () => {
|
||||
const ldk = new LightningLdkWallet();
|
||||
ldk.setSecret('ldk://abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about');
|
||||
assert.strictEqual(ldk.unwrapFirstExternalAddressFromMnemonics(), 'bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu');
|
||||
});
|
||||
});
|
Loading…
Add table
Reference in a new issue