GRPC
6 min
Monitoring with LND's GRPC interface
LND's gRPC interface provides the most efficient and feature-complete way to interact with your Lightning node. It offers bidirectional streaming, strongly-typed responses, and access to all LND functionality. gRPC is ideal for server-side applications that need low-latency, persistent connections with your node.
Setup
First, you'll need to download the LND proto files and install the necessary dependencies:
# Download the proto files for your LND version
curl -o lightning.proto https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/lightning.proto
# Install dependencies (JavaScript)
npm install @grpc/grpc-js @grpc/proto-loaderSubscribe to Single Invoice
Monitor updates for a specific invoice using its payment hash.
// Subscribe to a single invoice's state changes via gRPC
$invoiceHashHex = getenv('INVOICE_HASH_HEX') ?: '<invoice_hash_in_hex>';
$invoiceClient = new InvoicesClient($grpcHost, $opts);
// Prepare request with the invoice RHash (as raw bytes)
$invoiceReq = new Invoicesrpc\SubscribeSingleInvoiceRequest();
$invoiceReq->setRHash(hex2bin($invoiceHashHex));
// Initiate the RPC stream
$invoiceStream = $invoiceClient->SubscribeSingleInvoice($invoiceReq);
echo "Subscribed to invoice {$invoiceHashHex} (single invoice stream).\n";
// Read streamed Invoice updates
while (true) {
/** @var Lnrpc\Invoice $invoiceUpdate */
$invoiceUpdate = $invoiceStream->read();
if ($invoiceUpdate === null) {
// Stream ended (invoice reached a final state and stream closed)
echo "Invoice stream closed by server.\n";
break;
}
// Log the invoice's current state and amount paid
$state = $invoiceUpdate->getState(); // e.g., 0=OPEN, 1=SETTLED, 2=CANCELED
$settledFlag = $invoiceUpdate->getSettled(); // boolean
echo "π Invoice update: state=" . ($settledFlag ? 'SETTLED' : 'OPEN')
. ", amount_paid_sat=" . $invoiceUpdate->getAmtPaidSat() . PHP_EOL;
if ($settledFlag || $state === \Lnrpc\Invoice\InvoiceState::CANCELED) {
// Stop if invoice is settled or canceled
echo "Invoice is now final (settled or canceled). Ending subscription.\n";
break;
}
}Subscribe to All Invoices
Monitor all invoice activity on your node, with the ability to resume from a specific point.
// Subscribe to all invoice add/settle events via gRPC
$lightningClient = new LightningClient($grpcHost, $opts);
$subReq = new Lnrpc\InvoiceSubscription(); // empty request = subscribe from latest
// (Optionally, set add_index and/or settle_index on $subReq to resume from a specific point)
$invoiceStream = $lightningClient->SubscribeInvoices($subReq);
echo "Subscribed to all invoice events.\n";
// Stream all invoice updates continuously
while (true) {
$invoice = $invoiceStream->read();
if ($invoice === null) {
echo "Invoice event stream closed.\n";
break;
}
$hashHex = bin2hex($invoice->getRHash());
$stateStr = $invoice->getSettled() ? 'settled' : ($invoice->getState() === \Lnrpc\Invoice\InvoiceState::CANCELED ? 'canceled' : 'open');
echo "π£ Invoice #{$invoice->getAddIndex()} ({$hashHex}): {$stateStr}, value={$invoice->getValueSat()} sat\n";
// (Process invoice data as needed; this loop continues indefinitely)
}Track Single Payment
Monitor the progress of a specific outgoing payment.
// Track a single payment's progress via gRPC
$paymentHashHex = getenv('PAYMENT_HASH_HEX') ?: '<payment_hash_in_hex>';
$routerClient = new RouterClient($grpcHost, $opts);
$trackReq = new Routerrpc\TrackPaymentRequest();
$trackReq->setPaymentHash(hex2bin($paymentHashHex));
$trackReq->setNoInflightUpdates(false); // set true to only get final update
$paymentStream = $routerClient->TrackPaymentV2($trackReq);
echo "Tracking payment {$paymentHashHex} (gRPC)...\n";
// Read payment status updates
while (true) {
/** @var Lnrpc\Payment $payment */
$payment = $paymentStream->read();
if ($payment === null) {
echo "Payment stream closed by server.\n";
break;
}
$status = $payment->getStatus(); // an enum of type PaymentStatus
// Map status to human-readable form:
$statusName = ['UNKNOWN', 'IN_FLIGHT', 'SUCCEEDED', 'FAILED'][$status] ?? $status;
echo "π± Payment update: status={$statusName}";
if ($statusName === 'SUCCEEDED') {
$preimage = bin2hex($payment->getPaymentPreimage());
echo ", preimage={$preimage}\n";
echo "β
Payment succeeded, amount_sent={$payment->getValueSat()} sat, total_fee={$payment->getFeeSat()} sat\n";
break;
} elseif ($statusName === 'FAILED') {
echo "\nβ Payment failed (failure reason: {$payment->getFailureReason()}).\n";
break;
} else {
echo " (still in flight)\n";
// continue loop for more intermediate updates (e.g. HTLC attempts)
}
}Track All Payments
Monitor all outgoing payment activity on your node in real-time.
// Subscribe to updates for all in-flight payments via gRPC
$routerClient = new RouterClient($grpcHost, $opts);
$streamReq = new Routerrpc\TrackPaymentsRequest();
$streamReq->setNoInflightUpdates(false); // set true to only get final outcomes
$paymentsStream = $routerClient->TrackPayments($streamReq);
echo "Subscribed to all payments stream.\n";
while (true) {
/** @var Lnrpc\Payment $payment */
$payment = $paymentsStream->read();
if ($payment === null) {
echo "Payments stream closed (no more data).\n";
break;
}
$hashHex = $payment->getPaymentHash();
$status = $payment->getStatus();
$statusName = ['UNKNOWN','IN_FLIGHT','SUCCEEDED','FAILED'][$status] ?? $status;
echo "π€ Payment {$payment->getPaymentIndex()} (hash={$hashHex}): {$statusName}";
if ($statusName === 'SUCCEEDED') {
echo " (sent {$payment->getValueSat()} sat, fee {$payment->getFeeSat()} sat)";
} elseif ($statusName === 'FAILED') {
echo " (failed: reason={$payment->getFailureReason()})";
}
echo PHP_EOL;
// (The loop continues to stream updates for all ongoing/new payments)
}