As Lightning Network adoption continues to surge and more prominent companies like Paypal hire teams to get into the field, you shouldn't be missing out and join too!
While the network is still maturing, now is the best time to learn its ins and outs and become a part of an incredible community of enthusiastic and passionate people who shares ideals of financial independence and freedom.
Making it a good network citizen is an involving topic, so today, I want to focus on the mere basic: setting up a dev environment and connecting to it from Elixir.
Setting up the environment
The best way to hit the ground running is to set up a request (private blockchain network) with Lightning Polar. There is a wonderfully detailed guide here https://docs.lightning.engineering/lapps/guides/polar-lapps/local-cluster-setup-with-polar
In short - we need a network with two bitcoin nodes and two lightning network nodes. Each lightning network node will talk to its bitcoin node and each other. Bitcoin nodes will communicate through the base layer, and lightning nodes will communicate directly with each other.
After your network booted up (if not - stopping it and starting again as always helps), create a channel between lightning nodes.
I immediately paid almost half of the channel capacity to Bob, and liquidity became more evenly distributed - you can see how the color of a channel changed from green to green/blue. Channels are bidirectional and of a fixed size - you can use only as much sats as deposited in funding transactions.
And initially, all the funds were on Alice's side, so Bob could only receive them. Spreading liquidity between peers so both can use a channel to send and receive payments is called "balancing," In a testnet or mainnet, you can use Loop to send yourself payment from lightning network to base layer. That provides you with incoming liquidity for the cost of the on-chain transaction fee.
Now our network is ready to use!
Building an RPC client
LND offers both REST and RPC with mostly the same feature set but sometimes new features were available in the RPC sooner than in the REST. At least in the past.
LND RPC features are broken into several files and for the simplest case we can compile definitions only for the most used features described in https://github.com/lightningnetwork/lnd/tree/master/lnrpc/rpc.proto
I have this small script in the app's bin
folder
#!/bin/bash
OUTPUT_DIR=lib/grpc
mkdir -p .tmp/output
git clone https://github.com/googleapis/googleapis.git .tmp/googleapis
echo "===> Processing RPC Protobuf definition"
curl -s https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/lightning.proto -o .tmp/lightning.proto
protoc --elixir_out=plugins=grpc:./.tmp/output .tmp/lightning.proto --proto_path=.tmp/googleapis:.tmp
cp .tmp/output/lightning.pb.ex ${OUTPUT_DIR}/lightning.pb.ex
echo "===> Processing Router Protobuf definition"
curl -o .tmp/router.proto -s https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/routerrpc/router.proto
protoc --elixir_out=plugins=grpc:./.tmp/output .tmp/router.proto --proto_path=.tmp/googleapis:.tmp
cp .tmp/output/router.pb.ex ${OUTPUT_DIR}/router.pb.ex
rm -rf .tmp
Be sure to install the protobuf compiler before running it (brew install protobuf
if you're using Brew).
This script downloads Router and RPC protobouf definitions along with their dependencies builds them and places results in lib/grpc
You should see lightning.pb.ex
and lightning.pb.ex
in it after running this script.
Connecting to a node
To be able to talk to a node, we will need the following pieces:
- Node address and port - available at
- Macaroon file that defines our permissions
- TLS cert
You can get all those details on the "Connection" tab after clicking on a specific LND node in Polar.

I, personally, have the following lines in config/dev.exs
:
config :my_app,
lnd_ssl_crt: System.user_home() <> "/.polar/networks/1/volumes/lnd/alice/tls.cert",
lnd_admin_macaroon: System.user_home() <> "/.polar/networks/1/volumes/lnd/alice/data/chain/bitcoin/regtest/admin.macaroon"
Those paths should be identical across the machines as long as those are your two first nodes in the created network since they're bound to node names and first two nodes are always named "alice" and "bob" ;)
Add gRPC dependecy to your mix.exs
file:
def deps do
[
{:grpc, github: "elixir-grpc/grpc"},
# 2.9.0 fixes some important bugs, so it's better to use ~> 2.9.0
{:cowlib, "~> 2.9.0", override: true}
]
end
and we are ready for a launch!
Let's try out some of those juicy LND calls in our iex
shell :
First we need to connecto to a node using cert
cacertfile = Application.get_env(:my_app, :lnd_ssl_crt)
creds = GRPC.Credential.new(ssl: [cacertfile: cacertfile])
{:ok, conn} = GRPC.Stub.connect(Application.get_env(:my_app, :lightning_node_address), cred: creds)
With a connection in our hands, we can start interacting with LND services. For example, we can take a look at our balance:
macaroon = Application.get_env(:my_app, :lnd_admin_macaroon)|> File.read!() |> Base.encode16()
{:ok, ballance} = Lnrpc.Lightning.Stub.wallet_balance(conn, Lnrpc.WalletBalanceRequest.new(), metadata: %{macaroon: macaroon})
and you should be seeing something like this:
{:ok,
%Lnrpc.WalletBalanceResponse{
account_balance: %{
"default" => %Lnrpc.WalletAccountBalance{
confirmed_balance: 31337,
unconfirmed_balance: 0
}
},
confirmed_balance: 31337,
total_balance: 31337,
unconfirmed_balance: 0
}}
⚡ Congrats, you are ready for the lightning payments! ⚡