Api

Lease API

Service-backed channel liquidity, releases, and lease extensions

The Lease API exposes Hydra App's LiquidityService — a service-backed surface for provisioning channel liquidity, releasing channels (withdraw or cooperative close), and extending existing asset leases. Hydra App completes fee settlement and any required local confirmations internally before returning the final operation result.

JSON-RPC namespace: liquidity

Endpoints


Common types

ChannelLiquidityRequestOperation (one of)

Used by RequestChannelLiquidity and its estimator. Exactly one variant must be set.

VariantFieldsPurpose
opentarget_node_pubkey?, asset_liquidityOpen a new channel and provision liquidity
depositchannel_id, asset_liquidityDeposit into an existing channel
deposit_anytarget_node_pubkey?, asset_liquidityDeposit into any suitable channel; otherwise open a new one
open_or_deposittarget_node_pubkey?, asset_liquiditySame as deposit_any but biases toward opening

asset_liquidity is map<string, AssetLiquidity> keyed by asset_id.

AssetLiquidity:

FieldTypeDescription
server_amountDecimalStringProvider-side contribution
client_amountDecimalStringLocal/client-side contribution (used for service-broadcasted outbound funding or dual-funded requests)

ChannelReleaseOperation (one of)

Used by RequestChannelRelease and its estimator. Exactly one variant must be set.

VariantFieldsPurpose
withdrawchannel_id, asset_ids[]Withdraw the listed assets from the channel
cooperative_closechannel_idCooperatively close the channel

Fee payment (one of)

All RPCs that take a fee require exactly one fee_payment variant.

VariantFieldsWhen to use
onchain_fee_paymentOnchainFeePayment (UTXO or Account flow)Pay the service fee on chain
offchain_fee_paymentOffchainFeePayment (empty)Pay via invoice / offchain payment
dual_fund_fee_paymentDualFundFeePayment (empty)Settle the fee inside the dual-fund flow (provisioning RPCs only)

OnchainFeePayment (one of):

VariantFields
utxorefund_address
accountsender_address, refund_address?

dual_fund_fee_payment is only valid on RequestChannelLiquidity (and its estimator). RequestChannelRelease and RequestChannelLeaseExtension accept only onchain_fee_payment or offchain_fee_payment.


Request Channel Liquidity

Ask the liquidity service to provision channel liquidity on the client's behalf. Hydra App completes fee settlement and the required local confirmations before returning.

Method: RequestChannelLiquidity

Parameters:

NameTypeRequiredDescription
networkNetworkYESChannel network
operationChannelLiquidityRequestOperationYESProvisioning action (open / deposit / deposit_any / open_or_deposit)
lease_duration_secondsuint64ConditionalRequired when any requested asset has server_amount > 0; otherwise omit
tx_fee_rateFeeRateYESChain fee rate used to price the underlying transaction work
payment_networkNetworkYESNetwork used to pay the service fee
payment_asset_idstringYESAsset used to pay the service fee
fee_paymentone of OnchainFeePayment / OffchainFeePayment / DualFundFeePaymentYESExactly one

Response:

FieldTypeDescription
txidstringFunding transaction ID
channel_idstringChannel ID of the (new or updated) channel

Example Request (open channel, offchain fee):

import { LiquidityServiceClient } from './proto/LiquidityServiceClientPb'
import {
  RequestChannelLiquidityRequest,
  ChannelLiquidityRequestOperation,
  AssetLiquidity,
  OffchainFeePayment
} from './proto/liquidity_pb'

const client = new LiquidityServiceClient('http://localhost:50051')

const request = new RequestChannelLiquidityRequest()
request.setNetwork({ protocol: 1, id: '0a03cf40' }) // Bitcoin Signet

// Open new channel with 0.1 BTC on the server side
const open = new ChannelLiquidityRequestOperation.Open()
const liq = new AssetLiquidity()
liq.setServerAmount({ value: '0.1' })
liq.setClientAmount({ value: '0' })
open.getAssetLiquidityMap().set('BTC', liq)

const op = new ChannelLiquidityRequestOperation()
op.setOpen(open)
request.setOperation(op)

request.setLeaseDurationSeconds(2592000) // 30 days
request.setTxFeeRate({
  maxFeePerUnit: { value: '50' },
  priorityFeePerUnit: { value: '5' }
})
request.setPaymentNetwork({ protocol: 1, id: '0a03cf40' })
request.setPaymentAssetId('BTC')
request.setOffchainFeePayment(new OffchainFeePayment())

const response = await client.requestChannelLiquidity(request, {})
console.log('Channel:', response.getChannelId(), 'TX:', response.getTxid())

Example Response:

{
  "txid": "abc123...",
  "channel_id": "ch_def456"
}

Estimate Request Channel Liquidity Fee

Estimate the service fee for a channel liquidity request before executing it. Takes the same RequestChannelLiquidityRequest and returns just the fee.

Method: EstimateRequestChannelLiquidityFee

Parameters: Same as Request Channel Liquidity.

Response:

FieldTypeDescription
feeDecimalStringEstimated service fee, denominated in the asset identified by payment_asset_id

Example Request:

// Build the same RequestChannelLiquidityRequest as you would for the actual call
const response = await client.estimateRequestChannelLiquidityFee(request, {})
console.log('Estimated fee:', response.getFee()?.getValue())

Example Response:

{ "fee": "0.0001" }

Request Channel Release

Ask the liquidity service to withdraw assets from a channel or cooperatively close it. The service charges a fee and Hydra App settles it before observing the release update.

Method: RequestChannelRelease

Parameters:

NameTypeRequiredDescription
networkNetworkYESChannel network
operationChannelReleaseOperationYESwithdraw or cooperative_close
tx_fee_rateFeeRateYESChain fee rate for the underlying transaction
payment_networkNetworkYESNetwork used to pay the service fee
payment_asset_idstringYESAsset used to pay the service fee
fee_paymentone of OnchainFeePayment / OffchainFeePaymentYESExactly one (no dual-fund here)

Response:

FieldTypeDescription
txidstringRelease transaction ID

Example Request (cooperative close, offchain fee):

import {
  RequestChannelReleaseRequest,
  ChannelReleaseOperation,
  OffchainFeePayment
} from './proto/liquidity_pb'

const request = new RequestChannelReleaseRequest()
request.setNetwork({ protocol: 1, id: '0a03cf40' })

const close = new ChannelReleaseOperation.CooperativeClose()
close.setChannelId('ch_def456')
const op = new ChannelReleaseOperation()
op.setCooperativeClose(close)
request.setOperation(op)

request.setTxFeeRate({
  maxFeePerUnit: { value: '50' },
  priorityFeePerUnit: { value: '5' }
})
request.setPaymentNetwork({ protocol: 1, id: '0a03cf40' })
request.setPaymentAssetId('BTC')
request.setOffchainFeePayment(new OffchainFeePayment())

const response = await client.requestChannelRelease(request, {})
console.log('Release TX:', response.getTxid())

Example Response:

{ "txid": "rel_abc789..." }

Estimate Request Channel Release Fee

Estimate the service fee for a channel release before executing it.

Method: EstimateRequestChannelReleaseFee

Parameters: Same as Request Channel Release.

Response:

FieldTypeDescription
feeDecimalStringEstimated service fee

Example:

const response = await client.estimateRequestChannelReleaseFee(request, {})
console.log('Estimated fee:', response.getFee()?.getValue())

Example Response:

{ "fee": "0.00005" }

Request Channel Lease Extension

Extend the duration of an existing lease on a specific channel asset. lease_extension_seconds is an extension delta, not a replacement absolute expiry.

Method: RequestChannelLeaseExtension

Parameters:

NameTypeRequiredDescription
networkNetworkYESChannel network
channel_idstringYESChannel whose lease to extend
asset_idstringYESSpecific asset whose lease is being extended
lease_extension_secondsuint64YESDelta to add to the current expiry
payment_networkNetworkYESNetwork used to pay the service fee
payment_asset_idstringYESAsset used to pay the service fee
fee_paymentone of OnchainFeePayment / OffchainFeePaymentYESExactly one (no dual-fund here)

Response:

FieldTypeDescription
channel_idstringChannel that was extended
asset_idstringAsset whose lease was extended
expiry_timestamp_secondsint64New absolute expiry (Unix seconds)

Example Request:

import {
  RequestChannelLeaseExtensionRequest,
  OffchainFeePayment
} from './proto/liquidity_pb'

const request = new RequestChannelLeaseExtensionRequest()
request.setNetwork({ protocol: 1, id: '0a03cf40' })
request.setChannelId('ch_def456')
request.setAssetId('BTC')
request.setLeaseExtensionSeconds(2592000) // +30 days
request.setPaymentNetwork({ protocol: 1, id: '0a03cf40' })
request.setPaymentAssetId('BTC')
request.setOffchainFeePayment(new OffchainFeePayment())

const response = await client.requestChannelLeaseExtension(request, {})
console.log('New expiry:', response.getExpiryTimestampSeconds())

Example Response:

{
  "channel_id": "ch_def456",
  "asset_id": "BTC",
  "expiry_timestamp_seconds": 1735689600
}

Estimate Request Channel Lease Extension Fee

Estimate the service fee for a lease extension before executing it.

Method: EstimateRequestChannelLeaseExtensionFee

Parameters: Same as Request Channel Lease Extension.

Response:

FieldTypeDescription
feeDecimalStringEstimated service fee

Example:

const response = await client.estimateRequestChannelLeaseExtensionFee(request, {})
console.log('Estimated fee:', response.getFee()?.getValue())

Example Response:

{ "fee": "0.00002" }

Duration Reference

lease_duration_seconds and lease_extension_seconds are expressed in seconds.

DurationSecondsCalculation
1 hour3,60060 × 60
1 day86,40024 × 60 × 60
1 week604,8007 × 24 × 60 × 60
30 days2,592,00030 × 24 × 60 × 60
90 days7,776,00090 × 24 × 60 × 60
const days = 30
const leaseSeconds = days * 24 * 60 * 60 // 2,592,000

Choosing a fee payment method

VariantAvailable onWhen to use
offchain_fee_paymentLiquidity / Release / Lease ExtensionYou have offchain (channel) balance to pay the fee — fastest, lowest overhead
onchain_fee_payment (UTXO)Liquidity / Release / Lease Extension on Bitcoin-style protocolsYou're paying the fee from on-chain funds and need a refund address for change
onchain_fee_payment (Account)Liquidity / Release / Lease Extension on EVM-style protocolsYou're paying the fee from an EVM account; refund_address is optional
dual_fund_fee_paymentLiquidity onlyYou're contributing client-side funds (client_amount > 0) and want the fee settled inside the dual-fund flow

Common Workflows

Open a channel with server-provided liquidity (offchain fee)

async function openChannelWithLiquidity(
  client: LiquidityServiceClient,
  network: { protocol: number; id: string },
  assetId: string,
  serverAmount: string,
  leaseDays: number
) {
  const request = new RequestChannelLiquidityRequest()
  request.setNetwork(network)

  const open = new ChannelLiquidityRequestOperation.Open()
  const liq = new AssetLiquidity()
  liq.setServerAmount({ value: serverAmount })
  liq.setClientAmount({ value: '0' })
  open.getAssetLiquidityMap().set(assetId, liq)

  const op = new ChannelLiquidityRequestOperation()
  op.setOpen(open)
  request.setOperation(op)

  request.setLeaseDurationSeconds(leaseDays * 24 * 60 * 60)
  request.setTxFeeRate({
    maxFeePerUnit: { value: '50' },
    priorityFeePerUnit: { value: '5' }
  })
  request.setPaymentNetwork(network)
  request.setPaymentAssetId(assetId)
  request.setOffchainFeePayment(new OffchainFeePayment())

  // 1. Estimate first
  const estimate = await client.estimateRequestChannelLiquidityFee(request, {})
  console.log('Estimated fee:', estimate.getFee()?.getValue())

  // 2. Execute
  const result = await client.requestChannelLiquidity(request, {})
  return {
    channelId: result.getChannelId(),
    txid: result.getTxid()
  }
}

Cooperatively close a channel through the service

async function cooperativeClose(
  client: LiquidityServiceClient,
  network: { protocol: number; id: string },
  channelId: string,
  paymentAssetId: string
) {
  const request = new RequestChannelReleaseRequest()
  request.setNetwork(network)

  const close = new ChannelReleaseOperation.CooperativeClose()
  close.setChannelId(channelId)
  const op = new ChannelReleaseOperation()
  op.setCooperativeClose(close)
  request.setOperation(op)

  request.setTxFeeRate({
    maxFeePerUnit: { value: '50' },
    priorityFeePerUnit: { value: '5' }
  })
  request.setPaymentNetwork(network)
  request.setPaymentAssetId(paymentAssetId)
  request.setOffchainFeePayment(new OffchainFeePayment())

  const result = await client.requestChannelRelease(request, {})
  return result.getTxid()
}

Error Handling

Error CodeDescriptionSolution
INVALID_ARGUMENTMissing lease_duration_seconds while server_amount > 0, or no fee_payment variant setProvide a duration when leasing; pick exactly one fee payment variant
INVALID_ARGUMENTdual_fund_fee_payment used outside of RequestChannelLiquiditySwitch to onchain_fee_payment or offchain_fee_payment
RESOURCE_EXHAUSTEDInsufficient liquidity available on the provider sideReduce server_amount or retry later
FAILED_PRECONDITIONChannel does not exist, asset not present in channel, or release/extension not permittedVerify channel_id and asset_id against watchOnlyNode.GetChannel
UNAVAILABLELiquidity service temporarily unavailableRetry with exponential backoff

Best Practices

  1. Always estimate first. Call the matching Estimate* RPC before the operation.
  2. Provide lease_duration_seconds only when needed. Required if any asset has server_amount > 0; otherwise omit.
  3. Pick the right fee payment. Offchain is cheapest; dual-fund is only valid on RequestChannelLiquidity.
  4. Reuse channels when possible. deposit / deposit_any / open_or_deposit avoid the cost of opening a new channel.
  5. Treat lease_extension_seconds as a delta, not a target expiry.

← Back to API Reference | Next: Watch-Only Node API →


Copyright © 2025