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_amounts{}Cooperatively withdraw funds from the channel. The service broadcasts one withdrawal paying each asset's requested side(s) out, and pays the gas.
cooperative_closechannel_id, asset_ids[]Cooperatively close asset channels. Empty asset_ids closes every asset channel; otherwise only the listed ones.

withdraw.asset_amounts — per-asset, per-side split

Changed 2026-07-23 — breaking: this replaced the old asset_ids[] list.

asset_amounts is a map of asset_idAssetWithdrawAmounts:

FieldTypeDescription
server_amountAmount (optional)Releases the service's own balance to the service wallet
client_amountAmount (optional)Pays the client's balance out to the client's wallet

An absent side withdraws nothing; at least one side must be present. channel_id may be empty on fee estimates only (EstimateRequestChannelReleaseFee), and every requested side must then be an exact amount.

Migrating from asset_ids[]: a list entry "0xTOKEN" becomes a map entry "0xTOKEN": { client_amount: { exact: { amount: "..." } } } — you must now say how much and whose balance moves, not just which asset.

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
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.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
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.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.

Check the expiry before paying to extend. Leases extend automatically as a channel is used, so an actively-traded channel's expiry moves forward on its own — a market maker routing volume through a channel typically pays only the initial lease. Read the current expiry from AssetChannel.lease_expiry (on every watchOnlyNode.GetChannels / GetChannel read, added 2026-07-12), and extend manually only when it is actually running down. liquidity.GetLeaseExpiries returns the same information for all channels at once.

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.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.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.
  6. Watch AssetChannel.lease_expiry instead of assuming the duration you paid for. Usage extends a lease automatically, so the expiry moves; poll it (or liquidity.GetLeaseExpiries) and extend only when it is genuinely running down.
  7. On withdraw, name the side. asset_amounts needs server_amount and/or client_amount per asset — at least one — since 2026-07-23.

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


Copyright © 2025