Lease API
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
- Request Channel Liquidity
- Estimate Request Channel Liquidity Fee
- Request Channel Release
- Estimate Request Channel Release Fee
- Request Channel Lease Extension
- Estimate Request Channel Lease Extension Fee
Common types
ChannelLiquidityRequestOperation (one of)
Used by RequestChannelLiquidity and its estimator. Exactly one variant must be set.
| Variant | Fields | Purpose |
|---|---|---|
open | target_node_pubkey?, asset_liquidity | Open a new channel and provision liquidity |
deposit | channel_id, asset_liquidity | Deposit into an existing channel |
deposit_any | target_node_pubkey?, asset_liquidity | Deposit into any suitable channel; otherwise open a new one |
open_or_deposit | target_node_pubkey?, asset_liquidity | Same as deposit_any but biases toward opening |
asset_liquidity is map<string, AssetLiquidity> keyed by asset_id.
AssetLiquidity:
| Field | Type | Description |
|---|---|---|
server_amount | DecimalString | Provider-side contribution |
client_amount | DecimalString | Local/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.
| Variant | Fields | Purpose |
|---|---|---|
withdraw | channel_id, asset_ids[] | Withdraw the listed assets from the channel |
cooperative_close | channel_id | Cooperatively close the channel |
Fee payment (one of)
All RPCs that take a fee require exactly one fee_payment variant.
| Variant | Fields | When to use |
|---|---|---|
onchain_fee_payment | OnchainFeePayment (UTXO or Account flow) | Pay the service fee on chain |
offchain_fee_payment | OffchainFeePayment (empty) | Pay via invoice / offchain payment |
dual_fund_fee_payment | DualFundFeePayment (empty) | Settle the fee inside the dual-fund flow (provisioning RPCs only) |
OnchainFeePayment (one of):
| Variant | Fields |
|---|---|
utxo | refund_address |
account | sender_address, refund_address? |
dual_fund_fee_paymentis only valid onRequestChannelLiquidity(and its estimator).RequestChannelReleaseandRequestChannelLeaseExtensionaccept onlyonchain_fee_paymentoroffchain_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:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Channel network |
operation | ChannelLiquidityRequestOperation | YES | Provisioning action (open / deposit / deposit_any / open_or_deposit) |
lease_duration_seconds | uint64 | Conditional | Required when any requested asset has server_amount > 0; otherwise omit |
tx_fee_rate | FeeRate | YES | Chain fee rate used to price the underlying transaction work |
payment_network | Network | YES | Network used to pay the service fee |
payment_asset_id | string | YES | Asset used to pay the service fee |
fee_payment | one of OnchainFeePayment / OffchainFeePayment / DualFundFeePayment | YES | Exactly one |
Response:
| Field | Type | Description |
|---|---|---|
txid | string | Funding transaction ID |
channel_id | string | Channel 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:
| Field | Type | Description |
|---|---|---|
fee | DecimalString | Estimated 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:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Channel network |
operation | ChannelReleaseOperation | YES | withdraw or cooperative_close |
tx_fee_rate | FeeRate | YES | Chain fee rate for the underlying transaction |
payment_network | Network | YES | Network used to pay the service fee |
payment_asset_id | string | YES | Asset used to pay the service fee |
fee_payment | one of OnchainFeePayment / OffchainFeePayment | YES | Exactly one (no dual-fund here) |
Response:
| Field | Type | Description |
|---|---|---|
txid | string | Release 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:
| Field | Type | Description |
|---|---|---|
fee | DecimalString | Estimated 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:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Channel network |
channel_id | string | YES | Channel whose lease to extend |
asset_id | string | YES | Specific asset whose lease is being extended |
lease_extension_seconds | uint64 | YES | Delta to add to the current expiry |
payment_network | Network | YES | Network used to pay the service fee |
payment_asset_id | string | YES | Asset used to pay the service fee |
fee_payment | one of OnchainFeePayment / OffchainFeePayment | YES | Exactly one (no dual-fund here) |
Response:
| Field | Type | Description |
|---|---|---|
channel_id | string | Channel that was extended |
asset_id | string | Asset whose lease was extended |
expiry_timestamp_seconds | int64 | New 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:
| Field | Type | Description |
|---|---|---|
fee | DecimalString | Estimated 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.
| Duration | Seconds | Calculation |
|---|---|---|
| 1 hour | 3,600 | 60 × 60 |
| 1 day | 86,400 | 24 × 60 × 60 |
| 1 week | 604,800 | 7 × 24 × 60 × 60 |
| 30 days | 2,592,000 | 30 × 24 × 60 × 60 |
| 90 days | 7,776,000 | 90 × 24 × 60 × 60 |
const days = 30
const leaseSeconds = days * 24 * 60 * 60 // 2,592,000
Choosing a fee payment method
| Variant | Available on | When to use |
|---|---|---|
offchain_fee_payment | Liquidity / Release / Lease Extension | You have offchain (channel) balance to pay the fee — fastest, lowest overhead |
onchain_fee_payment (UTXO) | Liquidity / Release / Lease Extension on Bitcoin-style protocols | You'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 protocols | You're paying the fee from an EVM account; refund_address is optional |
dual_fund_fee_payment | Liquidity only | You'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 Code | Description | Solution |
|---|---|---|
INVALID_ARGUMENT | Missing lease_duration_seconds while server_amount > 0, or no fee_payment variant set | Provide a duration when leasing; pick exactly one fee payment variant |
INVALID_ARGUMENT | dual_fund_fee_payment used outside of RequestChannelLiquidity | Switch to onchain_fee_payment or offchain_fee_payment |
RESOURCE_EXHAUSTED | Insufficient liquidity available on the provider side | Reduce server_amount or retry later |
FAILED_PRECONDITION | Channel does not exist, asset not present in channel, or release/extension not permitted | Verify channel_id and asset_id against watchOnlyNode.GetChannel |
UNAVAILABLE | Liquidity service temporarily unavailable | Retry with exponential backoff |
Best Practices
- Always estimate first. Call the matching
Estimate*RPC before the operation. - Provide
lease_duration_secondsonly when needed. Required if any asset hasserver_amount > 0; otherwise omit. - Pick the right fee payment. Offchain is cheapest; dual-fund is only valid on
RequestChannelLiquidity. - Reuse channels when possible.
deposit/deposit_any/open_or_depositavoid the cost of opening a new channel. - Treat
lease_extension_secondsas a delta, not a target expiry.