NAV Navbar
  • Back to Developer Site
  • shell python javascript

    Lightning Loop gRPC API Reference

    Welcome to the gRPC API reference documentation for Lightning Loop.

    Lightning Loop is a non-custodial service offered by Lightning Labs to bridge on-chain and off-chain Bitcoin using submarine swaps. This repository is home to the Loop client and depends on the Lightning Network daemon lnd. All of lnd’s supported chain backends are fully supported when using the Loop client: Neutrino, Bitcoin Core, and btcd.

    In the current iteration of the Loop software, only off-chain to on-chain exchanges are supported, where the Loop client sends funds off-chain in exchange for the funds back on-chain.

    The service can be used in various situations:

    Future iterations of the Loop software will also allow on-chain to off-chain swaps. These swaps can be useful for additional use-cases:

    This site features the documentation for loop (CLI), and the API documentation for Python and JavaScript clients in order to communicate with a local loopd instance through gRPC. Currently, this communication is unauthenticated, so exposing this service to the internet is not recommended.

    The original client.proto file from which the gRPC documentation was generated can be found here.

    Alternatively, the REST documentation can be found here.

    LoopOut

    Simple RPC

    LoopOut initiates an loop out swap with the given parameters. The call returns after the swap has been set up with the swap server. From that point onwards, progress can be tracked via the SwapStatus stream that is returned from Monitor().

    
    # Attempts loop out the target amount into either the backing lnd's
    # wallet, or a targeted address.
    # The amount is to be specified in satoshis.
    # Optionally a BASE58/bech32 encoded bitcoin destination address may be
    # specified. If not specified, a new wallet address will be generated.
    
    $ loop out [command options] amt [addr]
    
    # --channel value  the 8-byte compact channel ID of the channel to loop out (default: 0)
    # --addr value     the optional address that the looped out funds should be sent to, if let blank the funds will go to lnd's wallet
    # --amt value      the amount in satoshis to loop out (default: 0)
    
    >>> import grpc
    >>> import client_pb2 as loop
    >>> import client_pb2_grpc as looprpc
    >>> channel = grpc.insecure_channel('localhost:11010')
    >>> stub = looprpc.SwapClientStub(channel)
    >>> request = loop.LoopOutRequest(
            amt=<int64>,
            dest=<string>,
            max_swap_routing_fee=<int64>,
            max_prepay_routing_fee=<int64>,
            max_swap_fee=<int64>,
            max_prepay_amt=<int64>,
            max_miner_fee=<int64>,
            loop_out_channel=<uint64>,
        )
    >>> response = stub.LoopOut(request)
    >>> print(response)
    { 
        "id": <string>,
        "htlc_address": <string>,
    }
    
    > var grpc = require('grpc');
    > var protoLoader = require('@grpc/proto-loader');
    > var packageDefinition = protoLoader.loadSync(
        './client.proto',
        {
           keepCase: true,
           longs: String,
           enums: String,
           defaults: true,
           oneofs: true
        },
      );
    > var looprpc = grpc.loadPackageDefinition(packageDefinition).looprpc;
    > var swapClient = new looprpc.SwapClient('localhost:11010', grpc.credentials.createInsecure());
    > var request = { 
        amt: <int64>, 
        dest: <string>, 
        max_swap_routing_fee: <int64>, 
        max_prepay_routing_fee: <int64>, 
        max_swap_fee: <int64>, 
        max_prepay_amt: <int64>, 
        max_miner_fee: <int64>, 
        loop_out_channel: <uint64>, 
      }
    > swapClient.loopOut(request, function(err, response) {
        console.log(response);
      })
    { 
        "id": <string>,
        "htlc_address": <string>,
    }
    

    gRPC Request: LoopOutRequest

    Parameter Type Description
    amt int64 Requested swap amount in sat. This does not include the swap and miner fee.
    dest string Base58 encoded destination address for the swap.
    max_swap_routing_fee int64 Maximum off-chain fee in msat that may be paid for payment to the server. This limit is applied during path finding. Typically this value is taken from the response of the GetQuote call.
    max_prepay_routing_fee int64 Maximum off-chain fee in msat that may be paid for payment to the server. This limit is applied during path finding. Typically this value is taken from the response of the GetQuote call.
    max_swap_fee int64 Maximum we are willing to pay the server for the swap. This value is not disclosed in the swap initiation call, but if the server asks for a higher fee, we abort the swap. Typically this value is taken from the response of the GetQuote call. It includes the prepay amount.
    max_prepay_amt int64 Maximum amount of the swap fee that may be charged as a prepayment.
    max_miner_fee int64 Maximum in on-chain fees that we are willing to spent. If we want to sweep the on-chain htlc and the fee estimate turns out higher than this value, we cancel the swap. If the fee estimate is lower, we publish the sweep tx. If the sweep tx is not confirmed, we are forced to ratchet up fees until it is swept. Possibly even exceeding max_miner_fee if we get close to the htlc timeout. Because the initial publication revealed the preimage, we have no other choice. The server may already have pulled the off-chain htlc. Only when the fee becomes higher than the swap amount, we can only wait for fees to come down and hope - if we are past the timeout - that the server is not publishing the revocation. max_miner_fee is typically taken from the response of the GetQuote call.
    loop_out_channel uint64 The channel to loop out, the channel to loop out is selected based on the lowest routing fee for the swap payment to the server.

    gRPC Response: SwapResponse

    Parameter Type Description
    id string Swap identifier to track status in the update stream that is returned from the Start() call. Currently this is the hash that locks the htlcs.
    htlc_address string The address of the on-chain htlc.

    LoopIn

    Simple RPC

    LoopIn initiates a loop in swap with the given parameters. The call returns after the swap has been set up with the swap server. From that point onwards, progress can be tracked via the SwapStatus stream that is returned from Monitor().

    
    
    >>> import grpc
    >>> import client_pb2 as loop
    >>> import client_pb2_grpc as looprpc
    >>> channel = grpc.insecure_channel('localhost:11010')
    >>> stub = looprpc.SwapClientStub(channel)
    >>> request = loop.LoopInRequest(
            amt=<int64>,
            max_swap_fee=<int64>,
            max_miner_fee=<int64>,
            loop_in_channel=<uint64>,
            external_htlc=<bool>,
        )
    >>> response = stub.LoopIn(request)
    >>> print(response)
    { 
        "id": <string>,
        "htlc_address": <string>,
    }
    
    > var grpc = require('grpc');
    > var protoLoader = require('@grpc/proto-loader');
    > var packageDefinition = protoLoader.loadSync(
        './client.proto',
        {
           keepCase: true,
           longs: String,
           enums: String,
           defaults: true,
           oneofs: true
        },
      );
    > var looprpc = grpc.loadPackageDefinition(packageDefinition).looprpc;
    > var swapClient = new looprpc.SwapClient('localhost:11010', grpc.credentials.createInsecure());
    > var request = { 
        amt: <int64>, 
        max_swap_fee: <int64>, 
        max_miner_fee: <int64>, 
        loop_in_channel: <uint64>, 
        external_htlc: <bool>, 
      }
    > swapClient.loopIn(request, function(err, response) {
        console.log(response);
      })
    { 
        "id": <string>,
        "htlc_address": <string>,
    }
    

    gRPC Request: LoopInRequest

    Parameter Type Description
    amt int64 Requested swap amount in sat. This does not include the swap and miner fee.
    max_swap_fee int64 Maximum we are willing to pay the server for the swap. This value is not disclosed in the swap initiation call, but if the server asks for a higher fee, we abort the swap. Typically this value is taken from the response of the GetQuote call.
    max_miner_fee int64 Maximum in on-chain fees that we are willing to spent. If we want to publish the on-chain htlc and the fee estimate turns out higher than this value, we cancel the swap. max_miner_fee is typically taken from the response of the GetQuote call.
    loop_in_channel uint64 The channel to loop in. If zero, the channel to loop in is selected based on the lowest routing fee for the swap payment from the server. Note: NOT YET IMPLEMENTED
    external_htlc bool If external_htlc is true, we expect the htlc to be published by an external actor.

    gRPC Response: SwapResponse

    Parameter Type Description
    id string Swap identifier to track status in the update stream that is returned from the Start() call. Currently this is the hash that locks the htlcs.
    htlc_address string The address of the on-chain htlc.

    Monitor

    Response-streaming RPC

    Monitor will return a stream of swap updates for currently active swaps. TODO: add MonitorSync version for REST clients.

    
    # Allows the user to monitor progress of any active swaps
    
    $ loop monitor [arguments...]
    
    
    >>> import grpc
    >>> import client_pb2 as loop
    >>> import client_pb2_grpc as looprpc
    >>> channel = grpc.insecure_channel('localhost:11010')
    >>> stub = looprpc.SwapClientStub(channel)
    >>> request = loop.MonitorRequest()
    >>> for response in stub.Monitor(request):
            print(response)
    { 
        "amt": <int64>,
        "id": <string>,
        "type": <SwapType>,
        "state": <SwapState>,
        "initiation_time": <int64>,
        "last_update_time": <int64>,
        "htlc_address": <string>,
        "cost_server": <int64>,
        "cost_onchain": <int64>,
        "cost_offchain": <int64>,
    }
    
    > var grpc = require('grpc');
    > var protoLoader = require('@grpc/proto-loader');
    > var packageDefinition = protoLoader.loadSync(
        './client.proto',
        {
           keepCase: true,
           longs: String,
           enums: String,
           defaults: true,
           oneofs: true
        },
      );
    > var looprpc = grpc.loadPackageDefinition(packageDefinition).looprpc;
    > var swapClient = new looprpc.SwapClient('localhost:11010', grpc.credentials.createInsecure());
    > var request = {}
    > var call = swapClient.monitor(request)
    > call.on('data', function(response) {
        // A response was received from the server.
        console.log(response);
      });
    > call.on('status', function(status) {
        // The current status of the stream.
      });
    > call.on('end', function() {
        // The server has closed the stream.
      });
    { 
        "amt": <int64>,
        "id": <string>,
        "type": <SwapType>,
        "state": <SwapState>,
        "initiation_time": <int64>,
        "last_update_time": <int64>,
        "htlc_address": <string>,
        "cost_server": <int64>,
        "cost_onchain": <int64>,
        "cost_offchain": <int64>,
    }
    

    gRPC Request: MonitorRequest

    This request has no parameters.

    gRPC Response: SwapStatus (Streaming)

    Parameter Type Description
    amt int64 Requested swap amount in sat. This does not include the swap and miner fee.
    id string Swap identifier to track status in the update stream that is returned from the Start() call. Currently this is the hash that locks the htlcs.
    type SwapType Swap type
    state SwapState State the swap is currently in, see State enum.
    initiation_time int64 Initiation time of the swap.
    last_update_time int64 Initiation time of the swap.
    htlc_address string Htlc address.
    cost_server int64 Swap server cost
    cost_onchain int64 On-chain transaction cost
    cost_offchain int64 Off-chain routing fees

    LoopOutTerms

    Simple RPC

    LoopOutTerms returns the terms that the server enforces for a loop out swap.

    
    # Display the current swap terms imposed by the server.
    
    $ loop terms [arguments...]
    
    
    >>> import grpc
    >>> import client_pb2 as loop
    >>> import client_pb2_grpc as looprpc
    >>> channel = grpc.insecure_channel('localhost:11010')
    >>> stub = looprpc.SwapClientStub(channel)
    >>> request = loop.TermsRequest()
    >>> response = stub.LoopOutTerms(request)
    >>> print(response)
    { 
        "swap_payment_dest": <string>,
        "swap_fee_base": <int64>,
        "swap_fee_rate": <int64>,
        "prepay_amt": <int64>,
        "min_swap_amount": <int64>,
        "max_swap_amount": <int64>,
        "cltv_delta": <int32>,
    }
    
    > var grpc = require('grpc');
    > var protoLoader = require('@grpc/proto-loader');
    > var packageDefinition = protoLoader.loadSync(
        './client.proto',
        {
           keepCase: true,
           longs: String,
           enums: String,
           defaults: true,
           oneofs: true
        },
      );
    > var looprpc = grpc.loadPackageDefinition(packageDefinition).looprpc;
    > var swapClient = new looprpc.SwapClient('localhost:11010', grpc.credentials.createInsecure());
    > var request = {}
    > swapClient.loopOutTerms(request, function(err, response) {
        console.log(response);
      })
    { 
        "swap_payment_dest": <string>,
        "swap_fee_base": <int64>,
        "swap_fee_rate": <int64>,
        "prepay_amt": <int64>,
        "min_swap_amount": <int64>,
        "max_swap_amount": <int64>,
        "cltv_delta": <int32>,
    }
    

    gRPC Request: TermsRequest

    This request has no parameters.

    gRPC Response: TermsResponse

    Parameter Type Description
    swap_payment_dest string The node pubkey where the swap payment needs to be paid to. This can be used to test connectivity before initiating the swap.
    swap_fee_base int64 The base fee for a swap (sat)
    swap_fee_rate int64 The fee rate for a swap (parts per million)
    prepay_amt int64 Required prepay amount
    min_swap_amount int64 Minimum swap amount (sat)
    max_swap_amount int64 Maximum swap amount (sat)
    cltv_delta int32 On-chain cltv expiry delta

    LoopOutQuote

    Simple RPC

    LoopOutQuote returns a quote for a loop out swap with the provided parameters.

    
    # Allows to determine the cost of a swap up front
    
    $ loop quote amt
    
    
    >>> import grpc
    >>> import client_pb2 as loop
    >>> import client_pb2_grpc as looprpc
    >>> channel = grpc.insecure_channel('localhost:11010')
    >>> stub = looprpc.SwapClientStub(channel)
    >>> request = loop.QuoteRequest(
            amt=<int64>,
        )
    >>> response = stub.LoopOutQuote(request)
    >>> print(response)
    { 
        "swap_fee": <int64>,
        "prepay_amt": <int64>,
        "miner_fee": <int64>,
    }
    
    > var grpc = require('grpc');
    > var protoLoader = require('@grpc/proto-loader');
    > var packageDefinition = protoLoader.loadSync(
        './client.proto',
        {
           keepCase: true,
           longs: String,
           enums: String,
           defaults: true,
           oneofs: true
        },
      );
    > var looprpc = grpc.loadPackageDefinition(packageDefinition).looprpc;
    > var swapClient = new looprpc.SwapClient('localhost:11010', grpc.credentials.createInsecure());
    > var request = { 
        amt: <int64>, 
      }
    > swapClient.loopOutQuote(request, function(err, response) {
        console.log(response);
      })
    { 
        "swap_fee": <int64>,
        "prepay_amt": <int64>,
        "miner_fee": <int64>,
    }
    

    gRPC Request: QuoteRequest

    Parameter Type Description
    amt int64 The amount to swap in satoshis.

    gRPC Response: QuoteResponse

    Parameter Type Description
    swap_fee int64 The fee that the swap server is charging for the swap.
    prepay_amt int64 The part of the swap fee that is requested as a prepayment.
    miner_fee int64 An estimate of the on-chain fee that needs to be paid to sweep the HTLC.

    GetLoopInTerms

    Simple RPC

    GetTerms returns the terms that the server enforces for swaps.

    
    
    >>> import grpc
    >>> import client_pb2 as loop
    >>> import client_pb2_grpc as looprpc
    >>> channel = grpc.insecure_channel('localhost:11010')
    >>> stub = looprpc.SwapClientStub(channel)
    >>> request = loop.TermsRequest()
    >>> response = stub.GetLoopInTerms(request)
    >>> print(response)
    { 
        "swap_payment_dest": <string>,
        "swap_fee_base": <int64>,
        "swap_fee_rate": <int64>,
        "prepay_amt": <int64>,
        "min_swap_amount": <int64>,
        "max_swap_amount": <int64>,
        "cltv_delta": <int32>,
    }
    
    > var grpc = require('grpc');
    > var protoLoader = require('@grpc/proto-loader');
    > var packageDefinition = protoLoader.loadSync(
        './client.proto',
        {
           keepCase: true,
           longs: String,
           enums: String,
           defaults: true,
           oneofs: true
        },
      );
    > var looprpc = grpc.loadPackageDefinition(packageDefinition).looprpc;
    > var swapClient = new looprpc.SwapClient('localhost:11010', grpc.credentials.createInsecure());
    > var request = {}
    > swapClient.getLoopInTerms(request, function(err, response) {
        console.log(response);
      })
    { 
        "swap_payment_dest": <string>,
        "swap_fee_base": <int64>,
        "swap_fee_rate": <int64>,
        "prepay_amt": <int64>,
        "min_swap_amount": <int64>,
        "max_swap_amount": <int64>,
        "cltv_delta": <int32>,
    }
    

    gRPC Request: TermsRequest

    This request has no parameters.

    gRPC Response: TermsResponse

    Parameter Type Description
    swap_payment_dest string The node pubkey where the swap payment needs to be paid to. This can be used to test connectivity before initiating the swap.
    swap_fee_base int64 The base fee for a swap (sat)
    swap_fee_rate int64 The fee rate for a swap (parts per million)
    prepay_amt int64 Required prepay amount
    min_swap_amount int64 Minimum swap amount (sat)
    max_swap_amount int64 Maximum swap amount (sat)
    cltv_delta int32 On-chain cltv expiry delta

    GetLoopInQuote

    Simple RPC

    GetQuote returns a quote for a swap with the provided parameters.

    
    
    >>> import grpc
    >>> import client_pb2 as loop
    >>> import client_pb2_grpc as looprpc
    >>> channel = grpc.insecure_channel('localhost:11010')
    >>> stub = looprpc.SwapClientStub(channel)
    >>> request = loop.QuoteRequest(
            amt=<int64>,
        )
    >>> response = stub.GetLoopInQuote(request)
    >>> print(response)
    { 
        "swap_fee": <int64>,
        "prepay_amt": <int64>,
        "miner_fee": <int64>,
    }
    
    > var grpc = require('grpc');
    > var protoLoader = require('@grpc/proto-loader');
    > var packageDefinition = protoLoader.loadSync(
        './client.proto',
        {
           keepCase: true,
           longs: String,
           enums: String,
           defaults: true,
           oneofs: true
        },
      );
    > var looprpc = grpc.loadPackageDefinition(packageDefinition).looprpc;
    > var swapClient = new looprpc.SwapClient('localhost:11010', grpc.credentials.createInsecure());
    > var request = { 
        amt: <int64>, 
      }
    > swapClient.getLoopInQuote(request, function(err, response) {
        console.log(response);
      })
    { 
        "swap_fee": <int64>,
        "prepay_amt": <int64>,
        "miner_fee": <int64>,
    }
    

    gRPC Request: QuoteRequest

    Parameter Type Description
    amt int64 The amount to swap in satoshis.

    gRPC Response: QuoteResponse

    Parameter Type Description
    swap_fee int64 The fee that the swap server is charging for the swap.
    prepay_amt int64 The part of the swap fee that is requested as a prepayment.
    miner_fee int64 An estimate of the on-chain fee that needs to be paid to sweep the HTLC.

    Messages

    LoopInRequest

    Parameter Type Description
    amt int64 Requested swap amount in sat. This does not include the swap and miner fee.
    max_swap_fee int64 Maximum we are willing to pay the server for the swap. This value is not disclosed in the swap initiation call, but if the server asks for a higher fee, we abort the swap. Typically this value is taken from the response of the GetQuote call.
    max_miner_fee int64 Maximum in on-chain fees that we are willing to spent. If we want to publish the on-chain htlc and the fee estimate turns out higher than this value, we cancel the swap. max_miner_fee is typically taken from the response of the GetQuote call.
    loop_in_channel uint64 The channel to loop in. If zero, the channel to loop in is selected based on the lowest routing fee for the swap payment from the server. Note: NOT YET IMPLEMENTED
    external_htlc bool If external_htlc is true, we expect the htlc to be published by an external actor.

    LoopOutRequest

    Parameter Type Description
    amt int64 Requested swap amount in sat. This does not include the swap and miner fee.
    dest string Base58 encoded destination address for the swap.
    max_swap_routing_fee int64 Maximum off-chain fee in msat that may be paid for payment to the server. This limit is applied during path finding. Typically this value is taken from the response of the GetQuote call.
    max_prepay_routing_fee int64 Maximum off-chain fee in msat that may be paid for payment to the server. This limit is applied during path finding. Typically this value is taken from the response of the GetQuote call.
    max_swap_fee int64 Maximum we are willing to pay the server for the swap. This value is not disclosed in the swap initiation call, but if the server asks for a higher fee, we abort the swap. Typically this value is taken from the response of the GetQuote call. It includes the prepay amount.
    max_prepay_amt int64 Maximum amount of the swap fee that may be charged as a prepayment.
    max_miner_fee int64 Maximum in on-chain fees that we are willing to spent. If we want to sweep the on-chain htlc and the fee estimate turns out higher than this value, we cancel the swap. If the fee estimate is lower, we publish the sweep tx. If the sweep tx is not confirmed, we are forced to ratchet up fees until it is swept. Possibly even exceeding max_miner_fee if we get close to the htlc timeout. Because the initial publication revealed the preimage, we have no other choice. The server may already have pulled the off-chain htlc. Only when the fee becomes higher than the swap amount, we can only wait for fees to come down and hope - if we are past the timeout - that the server is not publishing the revocation. max_miner_fee is typically taken from the response of the GetQuote call.
    loop_out_channel uint64 The channel to loop out, the channel to loop out is selected based on the lowest routing fee for the swap payment to the server.

    MonitorRequest

    This message has no parameters.

    QuoteRequest

    Parameter Type Description
    amt int64 The amount to swap in satoshis.

    QuoteResponse

    Parameter Type Description
    swap_fee int64 The fee that the swap server is charging for the swap.
    prepay_amt int64 The part of the swap fee that is requested as a prepayment.
    miner_fee int64 An estimate of the on-chain fee that needs to be paid to sweep the HTLC.

    SwapResponse

    Parameter Type Description
    id string Swap identifier to track status in the update stream that is returned from the Start() call. Currently this is the hash that locks the htlcs.
    htlc_address string The address of the on-chain htlc.

    SwapStatus

    Parameter Type Description
    amt int64 Requested swap amount in sat. This does not include the swap and miner fee.
    id string Swap identifier to track status in the update stream that is returned from the Start() call. Currently this is the hash that locks the htlcs.
    type SwapType Swap type
    state SwapState State the swap is currently in, see State enum.
    initiation_time int64 Initiation time of the swap.
    last_update_time int64 Initiation time of the swap.
    htlc_address string Htlc address.
    cost_server int64 Swap server cost
    cost_onchain int64 On-chain transaction cost
    cost_offchain int64 Off-chain routing fees

    TermsRequest

    This message has no parameters.

    TermsResponse

    Parameter Type Description
    swap_payment_dest string The node pubkey where the swap payment needs to be paid to. This can be used to test connectivity before initiating the swap.
    swap_fee_base int64 The base fee for a swap (sat)
    swap_fee_rate int64 The fee rate for a swap (parts per million)
    prepay_amt int64 Required prepay amount
    min_swap_amount int64 Minimum swap amount (sat)
    max_swap_amount int64 Maximum swap amount (sat)
    cltv_delta int32 On-chain cltv expiry delta

    Enums

    SwapState

    Name Value Description
    INITIATED 0 INITIATED is the initial state of a swap. At that point, the initiation call to the server has been made and the payment process has been started for the swap and prepayment invoices.
    PREIMAGE_REVEALED 1 PREIMAGE_REVEALED is reached when the sweep tx publication is first attempted. From that point on, we should consider the preimage to no longer be secret and we need to do all we can to get the sweep confirmed. This state will mostly coalesce with StateHtlcConfirmed, except in the case where we wait for fees to come down before we sweep.
    HTLC_PUBLISHED 2 HTLC_PUBLISHED is reached when the htlc tx has been published in a loop in swap.
    SUCCESS 3 SUCCESS is the final swap state that is reached when the sweep tx has the required confirmation depth.
    FAILED 4 FAILED is the final swap state for a failed swap with or without loss of the swap amount.
    INVOICE_SETTLED 5 INVOICE_SETTLED is reached when the swap invoice in a loop in swap has been paid, but we are still waiting for the htlc spend to confirm.

    SwapType

    Name Value Description
    LOOP_OUT 0 LOOP_OUT indicates an loop out swap (off-chain to on-chain)
    LOOP_IN 1 LOOP_IN indicates a loop in swap (on-chain to off-chain)