1384 lines
39 KiB
TypeScript
1384 lines
39 KiB
TypeScript
/**
|
|
* LDK Node Injection - Wrapper class for LDK Node native module
|
|
* Similar pattern to LndMobileInjection
|
|
*/
|
|
|
|
import { NativeModules, Platform } from 'react-native';
|
|
import RNFS from 'react-native-fs';
|
|
import { generateVssAuthHeaders } from '../utils/VssAuthUtils';
|
|
import type {
|
|
Network,
|
|
NodeStatus,
|
|
BalanceDetails,
|
|
ChannelDetails,
|
|
ClosedChannelDetails,
|
|
PaymentDetails,
|
|
PeerDetails,
|
|
LdkNodeEvent,
|
|
Lsps1OrderResponse,
|
|
Lsps1OrderStatus,
|
|
Lsps7ExtendableChannel,
|
|
Lsps7OrderResponse
|
|
} from './LdkNode.d';
|
|
|
|
const { LdkNodeModule } = NativeModules;
|
|
|
|
// ============================================================================
|
|
// Builder Functions
|
|
// ============================================================================
|
|
|
|
const createBuilder = async (): Promise<void> => {
|
|
return await LdkNodeModule.createBuilder();
|
|
};
|
|
|
|
const setNetwork = async (network: Network): Promise<void> => {
|
|
return await LdkNodeModule.setNetwork(network);
|
|
};
|
|
|
|
const setStorageDirPath = async (path: string): Promise<void> => {
|
|
return await LdkNodeModule.setStorageDirPath(path);
|
|
};
|
|
|
|
const setEsploraServer = async (serverUrl: string): Promise<void> => {
|
|
return await LdkNodeModule.setEsploraServer(serverUrl);
|
|
};
|
|
|
|
const setGossipSourceRgs = async (rgsServerUrl: string): Promise<void> => {
|
|
return await LdkNodeModule.setGossipSourceRgs(rgsServerUrl);
|
|
};
|
|
|
|
const setPathfindingScoresSource = async (url: string): Promise<void> => {
|
|
return await LdkNodeModule.setPathfindingScoresSource(url);
|
|
};
|
|
|
|
const setGossipSourceP2p = async (): Promise<void> => {
|
|
return await LdkNodeModule.setGossipSourceP2p();
|
|
};
|
|
|
|
const setListeningAddresses = async (addresses: string[]): Promise<void> => {
|
|
return await LdkNodeModule.setListeningAddresses(addresses);
|
|
};
|
|
|
|
const setLiquiditySourceLsps1 = async ({
|
|
nodeId,
|
|
address,
|
|
token
|
|
}: {
|
|
nodeId: string;
|
|
address: string;
|
|
token?: string | null;
|
|
}): Promise<void> => {
|
|
return await LdkNodeModule.setLiquiditySourceLsps1(
|
|
nodeId,
|
|
address,
|
|
token ?? null
|
|
);
|
|
};
|
|
|
|
const setLiquiditySourceLsps2 = async ({
|
|
nodeId,
|
|
address,
|
|
token
|
|
}: {
|
|
nodeId: string;
|
|
address: string;
|
|
token?: string | null;
|
|
}): Promise<void> => {
|
|
return await LdkNodeModule.setLiquiditySourceLsps2(
|
|
nodeId,
|
|
address,
|
|
token ?? null
|
|
);
|
|
};
|
|
|
|
const setLiquiditySourceLsps7 = async ({
|
|
nodeId,
|
|
address,
|
|
token
|
|
}: {
|
|
nodeId: string;
|
|
address: string;
|
|
token?: string | null;
|
|
}): Promise<void> => {
|
|
return await LdkNodeModule.setLiquiditySourceLsps7(
|
|
nodeId,
|
|
address,
|
|
token ?? null
|
|
);
|
|
};
|
|
|
|
const setTrustedPeers0conf = async (peers: string[]): Promise<void> => {
|
|
return await LdkNodeModule.setTrustedPeers0conf(peers);
|
|
};
|
|
|
|
const setVssServer = async (
|
|
vssUrl: string,
|
|
storeId: string,
|
|
headers?: Record<string, string>
|
|
): Promise<void> => {
|
|
return await LdkNodeModule.setVssServer(vssUrl, storeId, headers ?? null);
|
|
};
|
|
|
|
const setVssBuildTimeout = async (timeoutSeconds: number): Promise<void> => {
|
|
return await LdkNodeModule.setVssBuildTimeout(timeoutSeconds);
|
|
};
|
|
|
|
// ============================================================================
|
|
// Mnemonic Functions
|
|
// ============================================================================
|
|
|
|
const generateMnemonic = async (wordCount: number = 12): Promise<string> => {
|
|
const result: any = await LdkNodeModule.generateMnemonic(wordCount);
|
|
return result.mnemonic;
|
|
};
|
|
|
|
/**
|
|
* Native PBKDF2-SHA512 for BIP39 seed derivation.
|
|
* Returns the 64-byte seed as a hex string.
|
|
* Native implementation is ~500x faster than JS (~5ms vs ~3.4s).
|
|
*/
|
|
const mnemonicToSeed = async (
|
|
mnemonic: string,
|
|
passphrase?: string | null
|
|
): Promise<string> => {
|
|
return await LdkNodeModule.mnemonicToSeed(mnemonic, passphrase ?? null);
|
|
};
|
|
|
|
// ============================================================================
|
|
// Node Build Functions
|
|
// ============================================================================
|
|
|
|
const buildNode = async (
|
|
mnemonic: string,
|
|
passphrase?: string | null
|
|
): Promise<{ vssError?: string } | null> => {
|
|
return await LdkNodeModule.buildNode(mnemonic, passphrase ?? null);
|
|
};
|
|
|
|
// ============================================================================
|
|
// Node Lifecycle Functions
|
|
// ============================================================================
|
|
|
|
const start = async (): Promise<void> => {
|
|
return await LdkNodeModule.start();
|
|
};
|
|
|
|
const stop = async (): Promise<void> => {
|
|
return await LdkNodeModule.stop();
|
|
};
|
|
|
|
const syncWallets = async (): Promise<void> => {
|
|
return await LdkNodeModule.syncWallets();
|
|
};
|
|
|
|
const setPersistentMode = async (enabled: boolean): Promise<void> => {
|
|
if (Platform.OS !== 'android') return;
|
|
return await LdkNodeModule.setPersistentMode(enabled);
|
|
};
|
|
|
|
// ============================================================================
|
|
// Node Info Functions
|
|
// ============================================================================
|
|
|
|
const nodeId = async (): Promise<string> => {
|
|
const result: any = await LdkNodeModule.nodeId();
|
|
return result.nodeId;
|
|
};
|
|
|
|
const status = async (): Promise<NodeStatus> => {
|
|
return await LdkNodeModule.status();
|
|
};
|
|
|
|
const listBalances = async (): Promise<BalanceDetails> => {
|
|
return await LdkNodeModule.listBalances();
|
|
};
|
|
|
|
const networkGraphInfo = async (): Promise<{
|
|
channelCount: number;
|
|
nodeCount: number;
|
|
}> => {
|
|
return await LdkNodeModule.networkGraphInfo();
|
|
};
|
|
|
|
const resetNetworkGraph = async (): Promise<void> => {
|
|
return await LdkNodeModule.resetNetworkGraph();
|
|
};
|
|
|
|
const updateRgsSnapshot = async (): Promise<{ timestamp: number }> => {
|
|
return await LdkNodeModule.updateRgsSnapshot();
|
|
};
|
|
|
|
// ============================================================================
|
|
// Channel Functions
|
|
// ============================================================================
|
|
|
|
const listChannels = async (): Promise<ChannelDetails[]> => {
|
|
const result: any = await LdkNodeModule.listChannels();
|
|
return result.channels;
|
|
};
|
|
|
|
const listClosedChannels = async (): Promise<ClosedChannelDetails[]> => {
|
|
const result: any = await LdkNodeModule.listClosedChannels();
|
|
return result.channels;
|
|
};
|
|
|
|
const openChannel = async ({
|
|
nodeId,
|
|
address,
|
|
channelAmountSats,
|
|
pushToCounterpartyMsat,
|
|
announceChannel = false
|
|
}: {
|
|
nodeId: string;
|
|
address: string;
|
|
channelAmountSats: number;
|
|
pushToCounterpartyMsat?: number | null;
|
|
announceChannel?: boolean;
|
|
}): Promise<string> => {
|
|
const result: any = await LdkNodeModule.openChannel(
|
|
nodeId,
|
|
address,
|
|
channelAmountSats,
|
|
pushToCounterpartyMsat ?? 0,
|
|
announceChannel
|
|
);
|
|
return result.userChannelId;
|
|
};
|
|
|
|
const openChannelFundMax = async ({
|
|
nodeId,
|
|
address,
|
|
pushToCounterpartyMsat,
|
|
announceChannel = false,
|
|
utxos
|
|
}: {
|
|
nodeId: string;
|
|
address: string;
|
|
pushToCounterpartyMsat?: number | null;
|
|
announceChannel?: boolean;
|
|
utxos?: Array<{ txid: string; vout: number }> | null;
|
|
}): Promise<string> => {
|
|
const result: any = await LdkNodeModule.openChannelFundMax(
|
|
nodeId,
|
|
address,
|
|
pushToCounterpartyMsat ?? 0,
|
|
announceChannel,
|
|
utxos ?? null
|
|
);
|
|
return result.userChannelId;
|
|
};
|
|
|
|
const openChannelWithUtxos = async ({
|
|
nodeId,
|
|
address,
|
|
channelAmountSats,
|
|
pushToCounterpartyMsat,
|
|
announceChannel = false,
|
|
utxos
|
|
}: {
|
|
nodeId: string;
|
|
address: string;
|
|
channelAmountSats: number;
|
|
pushToCounterpartyMsat?: number | null;
|
|
announceChannel?: boolean;
|
|
utxos: Array<{ txid: string; vout: number }>;
|
|
}): Promise<string> => {
|
|
const result: any = await LdkNodeModule.openChannelWithUtxos(
|
|
nodeId,
|
|
address,
|
|
channelAmountSats,
|
|
pushToCounterpartyMsat ?? 0,
|
|
announceChannel,
|
|
utxos
|
|
);
|
|
return result.userChannelId;
|
|
};
|
|
|
|
const closeChannel = async ({
|
|
userChannelId,
|
|
counterpartyNodeId
|
|
}: {
|
|
userChannelId: string;
|
|
counterpartyNodeId: string;
|
|
}): Promise<void> => {
|
|
return await LdkNodeModule.closeChannel(userChannelId, counterpartyNodeId);
|
|
};
|
|
|
|
const forceCloseChannel = async ({
|
|
userChannelId,
|
|
counterpartyNodeId,
|
|
reason
|
|
}: {
|
|
userChannelId: string;
|
|
counterpartyNodeId: string;
|
|
reason?: string;
|
|
}): Promise<void> => {
|
|
return await LdkNodeModule.forceCloseChannel(
|
|
userChannelId,
|
|
counterpartyNodeId,
|
|
reason ?? ''
|
|
);
|
|
};
|
|
|
|
// ============================================================================
|
|
// On-chain Functions
|
|
// ============================================================================
|
|
|
|
const newOnchainAddress = async (): Promise<string> => {
|
|
const result: any = await LdkNodeModule.newOnchainAddress();
|
|
return result.address;
|
|
};
|
|
|
|
const sendToOnchainAddress = async ({
|
|
address,
|
|
amountSats
|
|
}: {
|
|
address: string;
|
|
amountSats: number;
|
|
}): Promise<string> => {
|
|
const result: any = await LdkNodeModule.sendToOnchainAddress(
|
|
address,
|
|
amountSats
|
|
);
|
|
return result.txid;
|
|
};
|
|
|
|
const sendAllToOnchainAddress = async (
|
|
address: string,
|
|
retainReserve: boolean = false
|
|
): Promise<string> => {
|
|
const result: any = await LdkNodeModule.sendAllToOnchainAddress(
|
|
address,
|
|
retainReserve
|
|
);
|
|
return result.txid;
|
|
};
|
|
|
|
export interface WalletUtxo {
|
|
txid: string;
|
|
vout: number;
|
|
value_sats: number;
|
|
address: string;
|
|
is_spent: boolean;
|
|
}
|
|
|
|
const listUtxos = async (): Promise<WalletUtxo[]> => {
|
|
const result: any = await LdkNodeModule.listUtxos();
|
|
return result.utxos;
|
|
};
|
|
|
|
const sendToOnchainAddressWithUtxos = async ({
|
|
address,
|
|
amountSats,
|
|
utxos
|
|
}: {
|
|
address: string;
|
|
amountSats: number;
|
|
utxos: Array<{ txid: string; vout: number }>;
|
|
}): Promise<string> => {
|
|
const result: any = await LdkNodeModule.sendToOnchainAddressWithUtxos(
|
|
address,
|
|
amountSats,
|
|
utxos
|
|
);
|
|
return result.txid;
|
|
};
|
|
|
|
const sendAllToOnchainAddressWithUtxos = async ({
|
|
address,
|
|
retainReserve = false,
|
|
utxos
|
|
}: {
|
|
address: string;
|
|
retainReserve?: boolean;
|
|
utxos: Array<{ txid: string; vout: number }>;
|
|
}): Promise<string> => {
|
|
const result: any = await LdkNodeModule.sendAllToOnchainAddressWithUtxos(
|
|
address,
|
|
retainReserve,
|
|
utxos
|
|
);
|
|
return result.txid;
|
|
};
|
|
|
|
// ============================================================================
|
|
// BOLT11 Payment Functions
|
|
// ============================================================================
|
|
|
|
const receiveBolt11 = async ({
|
|
amountMsat,
|
|
description,
|
|
expirySecs
|
|
}: {
|
|
amountMsat: number;
|
|
description: string;
|
|
expirySecs: number;
|
|
}): Promise<string> => {
|
|
const result = await LdkNodeModule.receiveBolt11(
|
|
amountMsat,
|
|
description,
|
|
expirySecs
|
|
);
|
|
return result.invoice;
|
|
};
|
|
|
|
const receiveVariableAmountBolt11 = async ({
|
|
description,
|
|
expirySecs
|
|
}: {
|
|
description: string;
|
|
expirySecs: number;
|
|
}): Promise<string> => {
|
|
const result = await LdkNodeModule.receiveVariableAmountBolt11(
|
|
description,
|
|
expirySecs
|
|
);
|
|
return result.invoice;
|
|
};
|
|
|
|
const sendBolt11 = async ({
|
|
invoice,
|
|
maxTotalRoutingFeeMsat,
|
|
maxPathCount,
|
|
paymentTimeoutSecs
|
|
}: {
|
|
invoice: string;
|
|
maxTotalRoutingFeeMsat?: number;
|
|
maxPathCount?: number;
|
|
paymentTimeoutSecs?: number;
|
|
}): Promise<string> => {
|
|
const result: any = await LdkNodeModule.sendBolt11(
|
|
invoice,
|
|
maxTotalRoutingFeeMsat ?? -1,
|
|
maxPathCount ?? -1,
|
|
paymentTimeoutSecs ?? -1
|
|
);
|
|
return result.paymentId;
|
|
};
|
|
|
|
const sendBolt11UsingAmount = async ({
|
|
invoice,
|
|
amountMsat,
|
|
maxTotalRoutingFeeMsat,
|
|
maxPathCount,
|
|
paymentTimeoutSecs
|
|
}: {
|
|
invoice: string;
|
|
amountMsat: number;
|
|
maxTotalRoutingFeeMsat?: number;
|
|
maxPathCount?: number;
|
|
paymentTimeoutSecs?: number;
|
|
}): Promise<string> => {
|
|
const result: any = await LdkNodeModule.sendBolt11UsingAmount(
|
|
invoice,
|
|
amountMsat,
|
|
maxTotalRoutingFeeMsat ?? -1,
|
|
maxPathCount ?? -1,
|
|
paymentTimeoutSecs ?? -1
|
|
);
|
|
return result.paymentId;
|
|
};
|
|
|
|
// ============================================================================
|
|
// Probe Functions
|
|
// ============================================================================
|
|
|
|
const sendBolt11Probes = async ({
|
|
invoice,
|
|
maxTotalRoutingFeeMsat,
|
|
maxPathCount
|
|
}: {
|
|
invoice: string;
|
|
maxTotalRoutingFeeMsat?: number;
|
|
maxPathCount?: number;
|
|
}): Promise<void> => {
|
|
await LdkNodeModule.sendBolt11Probes(
|
|
invoice,
|
|
maxTotalRoutingFeeMsat ?? -1,
|
|
maxPathCount ?? -1
|
|
);
|
|
};
|
|
|
|
const sendBolt11ProbesUsingAmount = async ({
|
|
invoice,
|
|
amountMsat,
|
|
maxTotalRoutingFeeMsat,
|
|
maxPathCount
|
|
}: {
|
|
invoice: string;
|
|
amountMsat: number;
|
|
maxTotalRoutingFeeMsat?: number;
|
|
maxPathCount?: number;
|
|
}): Promise<void> => {
|
|
await LdkNodeModule.sendBolt11ProbesUsingAmount(
|
|
invoice,
|
|
amountMsat,
|
|
maxTotalRoutingFeeMsat ?? -1,
|
|
maxPathCount ?? -1
|
|
);
|
|
};
|
|
|
|
// ============================================================================
|
|
// Spontaneous Payment Functions
|
|
// ============================================================================
|
|
|
|
const sendSpontaneousPayment = async ({
|
|
nodeId,
|
|
amountMsat,
|
|
maxTotalRoutingFeeMsat,
|
|
maxPathCount,
|
|
paymentTimeoutSecs
|
|
}: {
|
|
nodeId: string;
|
|
amountMsat: number;
|
|
maxTotalRoutingFeeMsat?: number;
|
|
maxPathCount?: number;
|
|
paymentTimeoutSecs?: number;
|
|
}): Promise<string> => {
|
|
const result: any = await LdkNodeModule.sendSpontaneousPayment(
|
|
nodeId,
|
|
amountMsat,
|
|
maxTotalRoutingFeeMsat ?? -1,
|
|
maxPathCount ?? -1,
|
|
paymentTimeoutSecs ?? -1
|
|
);
|
|
return result.paymentId;
|
|
};
|
|
|
|
// ============================================================================
|
|
// BOLT12 Payment Functions
|
|
// ============================================================================
|
|
|
|
const bolt12Receive = async ({
|
|
amountMsat,
|
|
description,
|
|
expirySecs
|
|
}: {
|
|
amountMsat: number;
|
|
description: string;
|
|
expirySecs: number;
|
|
}): Promise<{ offer: string; offerId: string }> => {
|
|
return await LdkNodeModule.bolt12Receive(
|
|
amountMsat,
|
|
description,
|
|
expirySecs
|
|
);
|
|
};
|
|
|
|
const bolt12ReceiveVariableAmount = async ({
|
|
description,
|
|
expirySecs
|
|
}: {
|
|
description: string;
|
|
expirySecs: number;
|
|
}): Promise<{ offer: string; offerId: string }> => {
|
|
return await LdkNodeModule.bolt12ReceiveVariableAmount(
|
|
description,
|
|
expirySecs
|
|
);
|
|
};
|
|
|
|
const bolt12Send = async ({
|
|
offer,
|
|
payerNote,
|
|
maxTotalRoutingFeeMsat,
|
|
maxPathCount,
|
|
paymentTimeoutSecs
|
|
}: {
|
|
offer: string;
|
|
payerNote?: string | null;
|
|
maxTotalRoutingFeeMsat?: number;
|
|
maxPathCount?: number;
|
|
paymentTimeoutSecs?: number;
|
|
}): Promise<string> => {
|
|
const result = await LdkNodeModule.bolt12Send(
|
|
offer,
|
|
payerNote ?? null,
|
|
maxTotalRoutingFeeMsat ?? -1,
|
|
maxPathCount ?? -1,
|
|
paymentTimeoutSecs ?? -1
|
|
);
|
|
return result.paymentId;
|
|
};
|
|
|
|
const bolt12SendUsingAmount = async ({
|
|
offer,
|
|
amountMsat,
|
|
payerNote,
|
|
maxTotalRoutingFeeMsat,
|
|
maxPathCount,
|
|
paymentTimeoutSecs
|
|
}: {
|
|
offer: string;
|
|
amountMsat: number;
|
|
payerNote?: string | null;
|
|
maxTotalRoutingFeeMsat?: number;
|
|
maxPathCount?: number;
|
|
paymentTimeoutSecs?: number;
|
|
}): Promise<string> => {
|
|
const result = await LdkNodeModule.bolt12SendUsingAmount(
|
|
offer,
|
|
amountMsat,
|
|
payerNote ?? null,
|
|
maxTotalRoutingFeeMsat ?? -1,
|
|
maxPathCount ?? -1,
|
|
paymentTimeoutSecs ?? -1
|
|
);
|
|
return result.paymentId;
|
|
};
|
|
|
|
const bolt12InitiateRefund = async ({
|
|
amountMsat,
|
|
expirySecs,
|
|
maxTotalRoutingFeeMsat,
|
|
maxPathCount,
|
|
paymentTimeoutSecs
|
|
}: {
|
|
amountMsat: number;
|
|
expirySecs: number;
|
|
maxTotalRoutingFeeMsat?: number;
|
|
maxPathCount?: number;
|
|
paymentTimeoutSecs?: number;
|
|
}): Promise<string> => {
|
|
const result = await LdkNodeModule.bolt12InitiateRefund(
|
|
amountMsat,
|
|
expirySecs,
|
|
maxTotalRoutingFeeMsat ?? -1,
|
|
maxPathCount ?? -1,
|
|
paymentTimeoutSecs ?? -1
|
|
);
|
|
return result.refund;
|
|
};
|
|
|
|
const bolt12RequestRefundPayment = async (
|
|
refundStr: string
|
|
): Promise<string> => {
|
|
const result = await LdkNodeModule.bolt12RequestRefundPayment(refundStr);
|
|
return result.invoice;
|
|
};
|
|
|
|
// ============================================================================
|
|
// Payment Functions
|
|
// ============================================================================
|
|
|
|
const listPayments = async (): Promise<PaymentDetails[]> => {
|
|
const result: any = await LdkNodeModule.listPayments();
|
|
return result.payments;
|
|
};
|
|
|
|
// ============================================================================
|
|
// Peer Functions
|
|
// ============================================================================
|
|
|
|
const connect = async ({
|
|
nodeId,
|
|
address,
|
|
persist = true
|
|
}: {
|
|
nodeId: string;
|
|
address: string;
|
|
persist?: boolean;
|
|
}): Promise<void> => {
|
|
return await LdkNodeModule.connect(nodeId, address, persist);
|
|
};
|
|
|
|
const disconnect = async (nodeId: string): Promise<void> => {
|
|
return await LdkNodeModule.disconnect(nodeId);
|
|
};
|
|
|
|
const listPeers = async (): Promise<PeerDetails[]> => {
|
|
const result: any = await LdkNodeModule.listPeers();
|
|
return result.peers;
|
|
};
|
|
|
|
// ============================================================================
|
|
// Event Functions
|
|
// ============================================================================
|
|
|
|
const nextEvent = async (): Promise<LdkNodeEvent | null> => {
|
|
const result: any = await LdkNodeModule.nextEvent();
|
|
return result.event;
|
|
};
|
|
|
|
const eventHandled = async (): Promise<void> => {
|
|
return await LdkNodeModule.eventHandled();
|
|
};
|
|
|
|
// ============================================================================
|
|
// LSPS1 Functions
|
|
// ============================================================================
|
|
|
|
const lsps1RequestChannel = async ({
|
|
lspBalanceSat,
|
|
clientBalanceSat,
|
|
channelExpiryBlocks,
|
|
announceChannel
|
|
}: {
|
|
lspBalanceSat: number;
|
|
clientBalanceSat: number;
|
|
channelExpiryBlocks: number;
|
|
announceChannel: boolean;
|
|
}): Promise<Lsps1OrderResponse> => {
|
|
return await LdkNodeModule.lsps1RequestChannel(
|
|
lspBalanceSat,
|
|
clientBalanceSat,
|
|
channelExpiryBlocks,
|
|
announceChannel
|
|
);
|
|
};
|
|
|
|
const lsps1CheckOrderStatus = async (
|
|
orderId: string
|
|
): Promise<Lsps1OrderStatus> => {
|
|
return await LdkNodeModule.lsps1CheckOrderStatus(orderId);
|
|
};
|
|
|
|
// ============================================================================
|
|
// LSPS7 Functions
|
|
// ============================================================================
|
|
|
|
const lsps7GetExtendableChannels = async (): Promise<
|
|
Lsps7ExtendableChannel[]
|
|
> => {
|
|
return await LdkNodeModule.lsps7GetExtendableChannels();
|
|
};
|
|
|
|
const lsps7CreateOrder = async ({
|
|
shortChannelId,
|
|
channelExtensionExpiryBlocks,
|
|
token,
|
|
refundOnchainAddress
|
|
}: {
|
|
shortChannelId: string;
|
|
channelExtensionExpiryBlocks: number;
|
|
token?: string | null;
|
|
refundOnchainAddress?: string | null;
|
|
}): Promise<Lsps7OrderResponse> => {
|
|
return await LdkNodeModule.lsps7CreateOrder(
|
|
shortChannelId,
|
|
channelExtensionExpiryBlocks,
|
|
token ?? null,
|
|
refundOnchainAddress ?? null
|
|
);
|
|
};
|
|
|
|
const lsps7CheckOrderStatus = async (
|
|
orderId: string
|
|
): Promise<Lsps7OrderResponse> => {
|
|
return await LdkNodeModule.lsps7CheckOrderStatus(orderId);
|
|
};
|
|
|
|
// ============================================================================
|
|
// Message Signing Functions
|
|
// ============================================================================
|
|
|
|
const signMessage = async (message: string): Promise<string> => {
|
|
const result = await LdkNodeModule.signMessage(message);
|
|
return result.signature;
|
|
};
|
|
|
|
const verifySignature = async ({
|
|
message,
|
|
signature,
|
|
publicKey
|
|
}: {
|
|
message: string;
|
|
signature: string;
|
|
publicKey: string;
|
|
}): Promise<boolean> => {
|
|
const result = await LdkNodeModule.verifySignature(
|
|
message,
|
|
signature,
|
|
publicKey
|
|
);
|
|
return result.valid;
|
|
};
|
|
|
|
// ============================================================================
|
|
// Recovery Functions
|
|
// ============================================================================
|
|
|
|
const sweepRemoteClosedOutputs = async ({
|
|
sweepAddress,
|
|
feeRateSatsPerVbyte,
|
|
sleepSeconds
|
|
}: {
|
|
sweepAddress: string;
|
|
feeRateSatsPerVbyte: number;
|
|
sleepSeconds: number;
|
|
}): Promise<string> => {
|
|
const result = await LdkNodeModule.sweepRemoteClosedOutputs(
|
|
sweepAddress,
|
|
feeRateSatsPerVbyte,
|
|
sleepSeconds
|
|
);
|
|
return result.txHex;
|
|
};
|
|
|
|
// ============================================================================
|
|
// Helper Functions
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Initialize and build a new LDK Node with common defaults
|
|
*/
|
|
const initializeNode = async ({
|
|
network,
|
|
storagePath,
|
|
esploraServerUrl,
|
|
mnemonic,
|
|
passphrase,
|
|
rgsServerUrl,
|
|
scorerUrl,
|
|
listeningAddresses,
|
|
lsps1Config,
|
|
trustedPeers0conf,
|
|
vssConfig,
|
|
vssKey
|
|
}: {
|
|
network: Network;
|
|
storagePath: string;
|
|
esploraServerUrl: string;
|
|
mnemonic: string;
|
|
passphrase?: string | null;
|
|
rgsServerUrl?: string;
|
|
scorerUrl?: string;
|
|
listeningAddresses?: string[];
|
|
lsps1Config?: {
|
|
nodeId: string;
|
|
address: string;
|
|
token?: string | null;
|
|
};
|
|
trustedPeers0conf?: string[];
|
|
vssConfig?: {
|
|
url: string;
|
|
storeId: string;
|
|
};
|
|
vssKey?: { privateKey: Uint8Array; publicKey: Uint8Array };
|
|
}): Promise<{ vssError?: string }> => {
|
|
const t0 = Date.now();
|
|
const elapsed = () => `${Date.now() - t0}ms`;
|
|
console.log('LDK Node: Initializing...');
|
|
await createBuilder();
|
|
await setNetwork(network);
|
|
console.log(`LDK Node: [${elapsed()}] Network set to ${network}`);
|
|
await setStorageDirPath(storagePath);
|
|
await setEsploraServer(esploraServerUrl);
|
|
console.log(
|
|
`LDK Node: [${elapsed()}] Esplora server set to ${esploraServerUrl}`
|
|
);
|
|
|
|
if (rgsServerUrl) {
|
|
await setGossipSourceRgs(rgsServerUrl);
|
|
console.log(`LDK Node: RGS server set to ${rgsServerUrl}`);
|
|
} else {
|
|
console.log('LDK Node: No RGS server configured, using P2P gossip');
|
|
}
|
|
|
|
if (scorerUrl) {
|
|
await setPathfindingScoresSource(scorerUrl);
|
|
console.log(`LDK Node: Scorer URL set to ${scorerUrl}`);
|
|
}
|
|
|
|
if (listeningAddresses && listeningAddresses.length > 0) {
|
|
await setListeningAddresses(listeningAddresses);
|
|
}
|
|
|
|
// Configure LSPS1 liquidity source if provided (for buying inbound liquidity)
|
|
if (lsps1Config && lsps1Config.nodeId && lsps1Config.address) {
|
|
try {
|
|
await setLiquiditySourceLsps1({
|
|
nodeId: lsps1Config.nodeId,
|
|
address: lsps1Config.address,
|
|
token: lsps1Config.token
|
|
});
|
|
console.log('LDK Node: LSPS1 liquidity source configured');
|
|
} catch (e) {
|
|
console.log(
|
|
'LDK Node: LSPS1 configuration failed, REST API will be used'
|
|
);
|
|
}
|
|
|
|
// Also configure LSPS2 with the same LSP for JIT channels
|
|
try {
|
|
await setLiquiditySourceLsps2({
|
|
nodeId: lsps1Config.nodeId,
|
|
address: lsps1Config.address,
|
|
token: lsps1Config.token
|
|
});
|
|
console.log('LDK Node: LSPS2 liquidity source configured');
|
|
} catch (e) {
|
|
console.log('LDK Node: LSPS2 configuration failed');
|
|
}
|
|
|
|
// Also configure LSPS7 with the same LSP for channel lease extensions
|
|
try {
|
|
await setLiquiditySourceLsps7({
|
|
nodeId: lsps1Config.nodeId,
|
|
address: lsps1Config.address,
|
|
token: lsps1Config.token
|
|
});
|
|
console.log('LDK Node: LSPS7 liquidity source configured');
|
|
} catch (e) {
|
|
console.log('LDK Node: LSPS7 configuration failed');
|
|
}
|
|
}
|
|
|
|
// Set trusted peers for zero-conf channels (e.g., LSP)
|
|
if (trustedPeers0conf && trustedPeers0conf.length > 0) {
|
|
await setTrustedPeers0conf(trustedPeers0conf);
|
|
console.log(
|
|
`LDK Node: Trusted peers for 0-conf set: ${trustedPeers0conf.length} peer(s)`
|
|
);
|
|
}
|
|
|
|
// Configure VSS (Versioned Storage Service) for cloud backup
|
|
if (vssConfig && vssConfig.url && vssConfig.storeId) {
|
|
let vssHeaders: Record<string, string> | undefined;
|
|
try {
|
|
const tAuth = Date.now();
|
|
// Pass pre-derived keypair to avoid a second PBKDF2 (~3.4s)
|
|
vssHeaders = generateVssAuthHeaders(
|
|
mnemonic,
|
|
passphrase ?? undefined,
|
|
vssKey
|
|
);
|
|
console.log(
|
|
`LDK Node: [${elapsed()}] VSS auth header generated (${
|
|
Date.now() - tAuth
|
|
}ms)`
|
|
);
|
|
} catch (e) {
|
|
console.warn(
|
|
`LDK Node: [${elapsed()}] Failed to generate VSS auth header:`,
|
|
e
|
|
);
|
|
}
|
|
const tVssSet = Date.now();
|
|
await setVssServer(vssConfig.url, vssConfig.storeId, vssHeaders);
|
|
console.log(
|
|
`LDK Node: [${elapsed()}] VSS server set to ${vssConfig.url} (${
|
|
Date.now() - tVssSet
|
|
}ms)`
|
|
);
|
|
}
|
|
|
|
// Use a longer timeout for restore-from-seed (no local DB yet) since
|
|
// every read falls through to VSS sequentially. Even existing nodes
|
|
// may have an incomplete local DB (e.g. after a previous fallback
|
|
// build), so the default is generous to avoid false alerts.
|
|
const localDbPath = `${storagePath}/ldk_node_data.sqlite`;
|
|
const hasLocalDb = await RNFS.exists(localDbPath);
|
|
const vssBuildTimeout = hasLocalDb ? 30 : 60;
|
|
await setVssBuildTimeout(vssBuildTimeout);
|
|
console.log(
|
|
`LDK Node: [${elapsed()}] VSS build timeout set to ${vssBuildTimeout}s (${
|
|
hasLocalDb ? 'existing node' : 'restore / first run'
|
|
})`
|
|
);
|
|
|
|
console.log(`LDK Node: [${elapsed()}] Starting buildNode...`);
|
|
const tBuild = Date.now();
|
|
const buildResult = await buildNode(mnemonic, passphrase);
|
|
const buildDuration = Date.now() - tBuild;
|
|
const vssError = buildResult?.vssError;
|
|
if (vssError) {
|
|
console.warn(
|
|
`LDK Node: [${elapsed()}] VSS failed after ${buildDuration}ms (${vssError}), using local storage`
|
|
);
|
|
} else {
|
|
console.log(
|
|
`LDK Node: [${elapsed()}] buildNode completed in ${buildDuration}ms`
|
|
);
|
|
}
|
|
console.log(
|
|
`LDK Node: [${elapsed()}] Build complete — total init time ${
|
|
Date.now() - t0
|
|
}ms`
|
|
);
|
|
return { vssError };
|
|
};
|
|
|
|
// ============================================================================
|
|
// Interface Definition
|
|
// ============================================================================
|
|
|
|
export interface ILdkNodeInjections {
|
|
builder: {
|
|
createBuilder: () => Promise<void>;
|
|
setNetwork: (network: Network) => Promise<void>;
|
|
setStorageDirPath: (path: string) => Promise<void>;
|
|
setEsploraServer: (serverUrl: string) => Promise<void>;
|
|
setGossipSourceRgs: (rgsServerUrl: string) => Promise<void>;
|
|
setPathfindingScoresSource: (url: string) => Promise<void>;
|
|
setGossipSourceP2p: () => Promise<void>;
|
|
setListeningAddresses: (addresses: string[]) => Promise<void>;
|
|
setLiquiditySourceLsps1: (params: {
|
|
nodeId: string;
|
|
address: string;
|
|
token?: string | null;
|
|
}) => Promise<void>;
|
|
setLiquiditySourceLsps2: (params: {
|
|
nodeId: string;
|
|
address: string;
|
|
token?: string | null;
|
|
}) => Promise<void>;
|
|
setTrustedPeers0conf: (peers: string[]) => Promise<void>;
|
|
setVssServer: (
|
|
vssUrl: string,
|
|
storeId: string,
|
|
headers?: Record<string, string>
|
|
) => Promise<void>;
|
|
setVssBuildTimeout: (timeoutSeconds: number) => Promise<void>;
|
|
};
|
|
mnemonic: {
|
|
generateMnemonic: (wordCount?: number) => Promise<string>;
|
|
};
|
|
crypto: {
|
|
mnemonicToSeed: (
|
|
mnemonic: string,
|
|
passphrase?: string | null
|
|
) => Promise<string>;
|
|
};
|
|
node: {
|
|
buildNode: (
|
|
mnemonic: string,
|
|
passphrase?: string | null
|
|
) => Promise<{ vssError?: string } | null>;
|
|
start: () => Promise<void>;
|
|
stop: () => Promise<void>;
|
|
syncWallets: () => Promise<void>;
|
|
setPersistentMode: (enabled: boolean) => Promise<void>;
|
|
nodeId: () => Promise<string>;
|
|
status: () => Promise<NodeStatus>;
|
|
listBalances: () => Promise<BalanceDetails>;
|
|
networkGraphInfo: () => Promise<{
|
|
channelCount: number;
|
|
nodeCount: number;
|
|
}>;
|
|
resetNetworkGraph: () => Promise<void>;
|
|
updateRgsSnapshot: () => Promise<{ timestamp: number }>;
|
|
};
|
|
channel: {
|
|
listChannels: () => Promise<ChannelDetails[]>;
|
|
listClosedChannels: () => Promise<ClosedChannelDetails[]>;
|
|
openChannel: (params: {
|
|
nodeId: string;
|
|
address: string;
|
|
channelAmountSats: number;
|
|
pushToCounterpartyMsat?: number | null;
|
|
announceChannel?: boolean;
|
|
}) => Promise<string>;
|
|
openChannelFundMax: (params: {
|
|
nodeId: string;
|
|
address: string;
|
|
pushToCounterpartyMsat?: number | null;
|
|
announceChannel?: boolean;
|
|
utxos?: Array<{ txid: string; vout: number }> | null;
|
|
}) => Promise<string>;
|
|
openChannelWithUtxos: (params: {
|
|
nodeId: string;
|
|
address: string;
|
|
channelAmountSats: number;
|
|
pushToCounterpartyMsat?: number | null;
|
|
announceChannel?: boolean;
|
|
utxos: Array<{ txid: string; vout: number }>;
|
|
}) => Promise<string>;
|
|
closeChannel: (params: {
|
|
userChannelId: string;
|
|
counterpartyNodeId: string;
|
|
}) => Promise<void>;
|
|
forceCloseChannel: (params: {
|
|
userChannelId: string;
|
|
counterpartyNodeId: string;
|
|
reason?: string;
|
|
}) => Promise<void>;
|
|
};
|
|
onchain: {
|
|
newOnchainAddress: () => Promise<string>;
|
|
sendToOnchainAddress: (params: {
|
|
address: string;
|
|
amountSats: number;
|
|
}) => Promise<string>;
|
|
sendAllToOnchainAddress: (
|
|
address: string,
|
|
retainReserve?: boolean
|
|
) => Promise<string>;
|
|
listUtxos: () => Promise<WalletUtxo[]>;
|
|
sendToOnchainAddressWithUtxos: (params: {
|
|
address: string;
|
|
amountSats: number;
|
|
utxos: Array<{ txid: string; vout: number }>;
|
|
}) => Promise<string>;
|
|
sendAllToOnchainAddressWithUtxos: (params: {
|
|
address: string;
|
|
retainReserve?: boolean;
|
|
utxos: Array<{ txid: string; vout: number }>;
|
|
}) => Promise<string>;
|
|
};
|
|
bolt11: {
|
|
receiveBolt11: (params: {
|
|
amountMsat: number;
|
|
description: string;
|
|
expirySecs: number;
|
|
}) => Promise<string>;
|
|
receiveVariableAmountBolt11: (params: {
|
|
description: string;
|
|
expirySecs: number;
|
|
}) => Promise<string>;
|
|
sendBolt11: (params: {
|
|
invoice: string;
|
|
maxTotalRoutingFeeMsat?: number;
|
|
maxPathCount?: number;
|
|
paymentTimeoutSecs?: number;
|
|
}) => Promise<string>;
|
|
sendBolt11UsingAmount: (params: {
|
|
invoice: string;
|
|
amountMsat: number;
|
|
maxTotalRoutingFeeMsat?: number;
|
|
maxPathCount?: number;
|
|
paymentTimeoutSecs?: number;
|
|
}) => Promise<string>;
|
|
sendBolt11Probes: (params: {
|
|
invoice: string;
|
|
maxTotalRoutingFeeMsat?: number;
|
|
maxPathCount?: number;
|
|
}) => Promise<void>;
|
|
sendBolt11ProbesUsingAmount: (params: {
|
|
invoice: string;
|
|
amountMsat: number;
|
|
maxTotalRoutingFeeMsat?: number;
|
|
maxPathCount?: number;
|
|
}) => Promise<void>;
|
|
};
|
|
spontaneous: {
|
|
sendSpontaneousPayment: (params: {
|
|
nodeId: string;
|
|
amountMsat: number;
|
|
maxTotalRoutingFeeMsat?: number;
|
|
maxPathCount?: number;
|
|
paymentTimeoutSecs?: number;
|
|
}) => Promise<string>;
|
|
};
|
|
bolt12: {
|
|
bolt12Receive: (params: {
|
|
amountMsat: number;
|
|
description: string;
|
|
expirySecs: number;
|
|
}) => Promise<{ offer: string; offerId: string }>;
|
|
bolt12ReceiveVariableAmount: (params: {
|
|
description: string;
|
|
expirySecs: number;
|
|
}) => Promise<{ offer: string; offerId: string }>;
|
|
bolt12Send: (params: {
|
|
offer: string;
|
|
payerNote?: string | null;
|
|
maxTotalRoutingFeeMsat?: number;
|
|
maxPathCount?: number;
|
|
paymentTimeoutSecs?: number;
|
|
}) => Promise<string>;
|
|
bolt12SendUsingAmount: (params: {
|
|
offer: string;
|
|
amountMsat: number;
|
|
payerNote?: string | null;
|
|
maxTotalRoutingFeeMsat?: number;
|
|
maxPathCount?: number;
|
|
paymentTimeoutSecs?: number;
|
|
}) => Promise<string>;
|
|
bolt12InitiateRefund: (params: {
|
|
amountMsat: number;
|
|
expirySecs: number;
|
|
maxTotalRoutingFeeMsat?: number;
|
|
maxPathCount?: number;
|
|
paymentTimeoutSecs?: number;
|
|
}) => Promise<string>;
|
|
bolt12RequestRefundPayment: (refundStr: string) => Promise<string>;
|
|
};
|
|
payments: {
|
|
listPayments: () => Promise<PaymentDetails[]>;
|
|
};
|
|
peers: {
|
|
connect: (params: {
|
|
nodeId: string;
|
|
address: string;
|
|
persist?: boolean;
|
|
}) => Promise<void>;
|
|
disconnect: (nodeId: string) => Promise<void>;
|
|
listPeers: () => Promise<PeerDetails[]>;
|
|
};
|
|
events: {
|
|
nextEvent: () => Promise<LdkNodeEvent | null>;
|
|
eventHandled: () => Promise<void>;
|
|
};
|
|
lsps1: {
|
|
requestChannel: (params: {
|
|
lspBalanceSat: number;
|
|
clientBalanceSat: number;
|
|
channelExpiryBlocks: number;
|
|
announceChannel: boolean;
|
|
}) => Promise<Lsps1OrderResponse>;
|
|
checkOrderStatus: (orderId: string) => Promise<Lsps1OrderStatus>;
|
|
};
|
|
lsps7: {
|
|
getExtendableChannels: () => Promise<Lsps7ExtendableChannel[]>;
|
|
createOrder: (params: {
|
|
shortChannelId: string;
|
|
channelExtensionExpiryBlocks: number;
|
|
token?: string | null;
|
|
refundOnchainAddress?: string | null;
|
|
}) => Promise<Lsps7OrderResponse>;
|
|
checkOrderStatus: (orderId: string) => Promise<Lsps7OrderResponse>;
|
|
};
|
|
signing: {
|
|
signMessage: (message: string) => Promise<string>;
|
|
verifySignature: (params: {
|
|
message: string;
|
|
signature: string;
|
|
publicKey: string;
|
|
}) => Promise<boolean>;
|
|
};
|
|
recovery: {
|
|
sweepRemoteClosedOutputs: (params: {
|
|
sweepAddress: string;
|
|
feeRateSatsPerVbyte: number;
|
|
sleepSeconds: number;
|
|
}) => Promise<string>;
|
|
};
|
|
utils: {
|
|
initializeNode: (params: {
|
|
network: Network;
|
|
storagePath: string;
|
|
esploraServerUrl: string;
|
|
mnemonic: string;
|
|
passphrase?: string | null;
|
|
rgsServerUrl?: string;
|
|
scorerUrl?: string;
|
|
listeningAddresses?: string[];
|
|
lsps1Config?: {
|
|
nodeId: string;
|
|
address: string;
|
|
token?: string | null;
|
|
};
|
|
trustedPeers0conf?: string[];
|
|
vssConfig?: {
|
|
url: string;
|
|
storeId: string;
|
|
};
|
|
vssKey?: {
|
|
privateKey: Uint8Array;
|
|
publicKey: Uint8Array;
|
|
};
|
|
}) => Promise<{ vssError?: string }>;
|
|
};
|
|
}
|
|
|
|
// ============================================================================
|
|
// Export
|
|
// ============================================================================
|
|
|
|
const LdkNodeInjection: ILdkNodeInjections = {
|
|
builder: {
|
|
createBuilder,
|
|
setNetwork,
|
|
setStorageDirPath,
|
|
setEsploraServer,
|
|
setGossipSourceRgs,
|
|
setPathfindingScoresSource,
|
|
setGossipSourceP2p,
|
|
setListeningAddresses,
|
|
setLiquiditySourceLsps1,
|
|
setLiquiditySourceLsps2,
|
|
setTrustedPeers0conf,
|
|
setVssServer,
|
|
setVssBuildTimeout
|
|
},
|
|
mnemonic: {
|
|
generateMnemonic
|
|
},
|
|
crypto: {
|
|
mnemonicToSeed
|
|
},
|
|
node: {
|
|
buildNode,
|
|
start,
|
|
stop,
|
|
syncWallets,
|
|
setPersistentMode,
|
|
nodeId,
|
|
status,
|
|
listBalances,
|
|
networkGraphInfo,
|
|
resetNetworkGraph,
|
|
updateRgsSnapshot
|
|
},
|
|
channel: {
|
|
listChannels,
|
|
listClosedChannels,
|
|
openChannel,
|
|
openChannelFundMax,
|
|
openChannelWithUtxos,
|
|
closeChannel,
|
|
forceCloseChannel
|
|
},
|
|
onchain: {
|
|
newOnchainAddress,
|
|
sendToOnchainAddress,
|
|
sendAllToOnchainAddress,
|
|
listUtxos,
|
|
sendToOnchainAddressWithUtxos,
|
|
sendAllToOnchainAddressWithUtxos
|
|
},
|
|
bolt11: {
|
|
receiveBolt11,
|
|
receiveVariableAmountBolt11,
|
|
sendBolt11,
|
|
sendBolt11UsingAmount,
|
|
sendBolt11Probes,
|
|
sendBolt11ProbesUsingAmount
|
|
},
|
|
spontaneous: {
|
|
sendSpontaneousPayment
|
|
},
|
|
bolt12: {
|
|
bolt12Receive,
|
|
bolt12ReceiveVariableAmount,
|
|
bolt12Send,
|
|
bolt12SendUsingAmount,
|
|
bolt12InitiateRefund,
|
|
bolt12RequestRefundPayment
|
|
},
|
|
payments: {
|
|
listPayments
|
|
},
|
|
peers: {
|
|
connect,
|
|
disconnect,
|
|
listPeers
|
|
},
|
|
events: {
|
|
nextEvent,
|
|
eventHandled
|
|
},
|
|
lsps1: {
|
|
requestChannel: lsps1RequestChannel,
|
|
checkOrderStatus: lsps1CheckOrderStatus
|
|
},
|
|
lsps7: {
|
|
getExtendableChannels: lsps7GetExtendableChannels,
|
|
createOrder: lsps7CreateOrder,
|
|
checkOrderStatus: lsps7CheckOrderStatus
|
|
},
|
|
signing: {
|
|
signMessage,
|
|
verifySignature
|
|
},
|
|
recovery: {
|
|
sweepRemoteClosedOutputs
|
|
},
|
|
utils: {
|
|
initializeNode
|
|
}
|
|
};
|
|
|
|
export default LdkNodeInjection;
|