Api

Rental API

Inbound liquidity rental

The Rental API allows you to rent inbound liquidity from liquidity providers without managing channels yourself.

Endpoints


Get Rental Node Info

Get general rental configuration from the node including supported assets and duration limits.

Method: GetRentalNodeInfo

Parameters: None

Response:

FieldTypeDescription
min_duration_secondsuint64Minimum rental duration in seconds
max_duration_secondsuint64Maximum rental duration in seconds
min_capacity_usdDecimalStringMinimum rental capacity in USD
max_capacity_usdDecimalStringMaximum rental capacity in USD
rental_assetsRentalAssetConfig[]Available assets for rental

RentalAssetConfig:

FieldTypeDescription
networkNetworkNetwork where asset exists
asset_idstringAsset identifier
rental_fee_ratioDecimalStringFee ratio (e.g., "0.001" = 0.1%)

Example Request:

TypeScript
import { RentalServiceClient } from './proto/RentalServiceClientPb'
import { GetRentalNodeInfoRequest } from './proto/rental_pb'

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

const request = new GetRentalNodeInfoRequest()
const response = await client.getRentalNodeInfo(request, {})

console.log('Duration range:',
  response.getMinDurationSeconds(),
  'to',
  response.getMaxDurationSeconds(),
  'seconds'
)

const assets = response.getRentalAssetsList()
assets.forEach(asset => {
  console.log('Asset:', asset.getAssetId(), 'Fee:', asset.getRentalFeeRatio())
})
Go
import (
    "context"
    "fmt"
    "log"
    pb "github.com/hydra/hydra-go/proto"
    "google.golang.org/grpc"
)

conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
if err != nil {
    log.Fatalf("Failed to connect: %v", err)
}
defer conn.Close()

client := pb.NewRentalServiceClient(conn)

req := &pb.GetRentalNodeInfoRequest{}
resp, err := client.GetRentalNodeInfo(context.Background(), req)
if err != nil {
    log.Fatalf("GetRentalNodeInfo failed: %v", err)
}

fmt.Printf("Duration range: %d to %d seconds\n",
    resp.MinDurationSeconds,
    resp.MaxDurationSeconds)

for _, asset := range resp.RentalAssets {
    fmt.Printf("Asset: %s, Fee: %s\n", asset.AssetId, asset.RentalFeeRatio)
}
Rust
use hydra_api::rental_service_client::RentalServiceClient;
use hydra_api::GetRentalNodeInfoRequest;
use tonic::transport::Channel;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let channel = Channel::from_static("http://localhost:50051")
        .connect()
        .await?;

    let mut client = RentalServiceClient::new(channel);

    let request = tonic::Request::new(GetRentalNodeInfoRequest {});
    let response = client.get_rental_node_info(request).await?;
    let info = response.into_inner();

    println!("Duration range: {} to {} seconds",
        info.min_duration_seconds,
        info.max_duration_seconds);

    for asset in info.rental_assets {
        println!("Asset: {}, Fee: {}", asset.asset_id, asset.rental_fee_ratio);
    }

    Ok(())
}

Example Response:

{
  "min_duration_seconds": "86400",
  "max_duration_seconds": "7776000",
  "min_capacity_usd": "100",
  "max_capacity_usd": "10000",
  "rental_assets": [
    {
      "network": {
        "protocol": 0,
        "chain_id": "0",
        "name": "Bitcoin"
      },
      "asset_id": "BTC",
      "rental_fee_ratio": "0.001"
    },
    {
      "network": {
        "protocol": 1,
        "chain_id": "1",
        "name": "Ethereum"
      },
      "asset_id": "0x0000000000000000000000000000000000000000",
      "rental_fee_ratio": "0.0012"
    }
  ]
}

Get Rentable Asset Info

Get detailed rental information for a specific asset.

Method: GetRentableAssetInfo

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork to query
asset_idstringYESAsset identifier

Response:

FieldTypeDescription
available_liquidityDecimalStringCurrently available liquidity
min_capacityDecimalStringMinimum rentable amount
max_capacityDecimalStringMaximum rentable amount
min_duration_secondsuint64Minimum rental duration in seconds
max_duration_secondsuint64Maximum rental duration in seconds
rental_fee_ratioDecimalStringFee as ratio of amount

Example Request:

TypeScript
const request = new GetRentableAssetInfoRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setAssetId('BTC')

const response = await client.getRentableAssetInfo(request, {})
console.log('Available:', response.getAvailableLiquidity())
console.log('Min:', response.getMinCapacity())
console.log('Max:', response.getMaxCapacity())
console.log('Fee ratio:', response.getRentalFeeRatio())
Go
req := &pb.GetRentableAssetInfoRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    AssetId: "BTC",
}

resp, err := client.GetRentableAssetInfo(context.Background(), req)
if err != nil {
    log.Fatalf("GetRentableAssetInfo failed: %v", err)
}

fmt.Printf("Available: %s\n", resp.AvailableLiquidity)
fmt.Printf("Min: %s\n", resp.MinCapacity)
fmt.Printf("Max: %s\n", resp.MaxCapacity)
fmt.Printf("Fee ratio: %s\n", resp.RentalFeeRatio)
Rust
use hydra_api::GetRentableAssetInfoRequest;

let request = tonic::Request::new(GetRentableAssetInfoRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    asset_id: "BTC".to_string(),
});

let response = client.get_rentable_asset_info(request).await?;
let info = response.into_inner();

println!("Available: {}", info.available_liquidity);
println!("Min: {}", info.min_capacity);
println!("Max: {}", info.max_capacity);
println!("Fee ratio: {}", info.rental_fee_ratio);

Example Response:

{
  "available_liquidity": "500000000",
  "min_capacity": "1000000",
  "max_capacity": "100000000",
  "min_duration_seconds": "86400",
  "max_duration_seconds": "2592000",
  "rental_fee_ratio": "0.001"
}

Estimate Rent Channel Fee

Estimate the rental fee before executing.

Method: EstimateRentChannelFee

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork to rent on
asset_idstringYESAsset to rent
lifetime_secondsuint64YESRental lifetime in seconds
amountDecimalStringYESAmount of inbound liquidity
rental_optionRentalOptionYESPayment or DualFund option

RentalOption (one of):

Payment Option

Use for ONCHAIN or OFFCHAIN payment methods.

FieldTypeDescription
rental_tx_fee_rateFeeRateTransaction fee rate (for ONCHAIN)
payment_networkNetworkNetwork for fee payment
payment_asset_idstringAsset used for fee payment
payment_methodPaymentMethodONCHAIN (0) or OFFCHAIN (1)

PaymentMethod Enum:

ValueDescriptionStatus
ONCHAIN (0)Pay rental fee with onchain transaction⚠️ Not currently supported
OFFCHAIN (1)Pay rental fee with Lightning payment✅ Supported

DualFund Option

Use for dual-funded channel rental.

FieldTypeDescription
self_amountDecimalStringYour contribution to the channel

Response:

FieldTypeDescription
feeDecimalStringEstimated rental fee

Example Request (OFFCHAIN Payment):

TypeScript
const request = new EstimateRentChannelFeeRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setAssetId('BTC')
request.setLifetimeSeconds(2592000) // 30 days
request.setAmount('10000000') // 0.1 BTC

// Using Payment option with OFFCHAIN method
request.setRentalOption({
  payment: {
    paymentNetwork: { protocol: 0, chainId: '0', name: 'Bitcoin' },
    paymentAssetId: 'BTC',
    paymentMethod: 1 // OFFCHAIN
  }
})

const response = await client.estimateRentChannelFee(request, {})
console.log('Rental fee:', response.getFee())
Go
req := &pb.EstimateRentChannelFeeRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    AssetId:         "BTC",
    LifetimeSeconds: 2592000, // 30 days
    Amount:          "10000000", // 0.1 BTC
    RentalOption: &pb.RentalOption{
        Option: &pb.RentalOption_Payment{
            Payment: &pb.RentalOption_PaymentOption{
                PaymentNetwork: &pb.Network{
                    Protocol: 0,
                    ChainId:  "0",
                    Name:     "Bitcoin",
                },
                PaymentAssetId: "BTC",
                PaymentMethod:  pb.PaymentMethod_OFFCHAIN,
            },
        },
    },
}

resp, err := client.EstimateRentChannelFee(context.Background(), req)
if err != nil {
    log.Fatalf("EstimateRentChannelFee failed: %v", err)
}

fmt.Printf("Rental fee: %s\n", resp.Fee)
Rust
use hydra_api::{EstimateRentChannelFeeRequest, RentalOption, rental_option, PaymentMethod};

let request = tonic::Request::new(EstimateRentChannelFeeRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    asset_id: "BTC".to_string(),
    lifetime_seconds: 2592000, // 30 days
    amount: "10000000".to_string(), // 0.1 BTC
    rental_option: Some(RentalOption {
        option: Some(rental_option::Option::Payment(
            rental_option::PaymentOption {
                payment_network: Some(Network {
                    protocol: 0,
                    chain_id: "0".to_string(),
                    name: "Bitcoin".to_string(),
                }),
                payment_asset_id: "BTC".to_string(),
                payment_method: PaymentMethod::Offchain as i32,
                ..Default::default()
            }
        )),
    }),
});

let response = client.estimate_rent_channel_fee(request).await?;
println!("Rental fee: {}", response.into_inner().fee);

Example Request (DualFund):

TypeScript
const request = new EstimateRentChannelFeeRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setAssetId('BTC')
request.setLifetimeSeconds(2592000)
request.setAmount('10000000')

// Using DualFund option
request.setRentalOption({
  dualFund: {
    selfAmount: '1000000' // Your 0.01 BTC contribution
  }
})

const response = await client.estimateRentChannelFee(request, {})
console.log('Rental fee:', response.getFee())
Go
req := &pb.EstimateRentChannelFeeRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    AssetId:         "BTC",
    LifetimeSeconds: 2592000,
    Amount:          "10000000",
    RentalOption: &pb.RentalOption{
        Option: &pb.RentalOption_DualFund{
            DualFund: &pb.RentalOption_DualFundOption{
                SelfAmount: "1000000", // Your 0.01 BTC contribution
            },
        },
    },
}

resp, err := client.EstimateRentChannelFee(context.Background(), req)
if err != nil {
    log.Fatalf("EstimateRentChannelFee failed: %v", err)
}

fmt.Printf("Rental fee: %s\n", resp.Fee)
Rust
use hydra_api::{EstimateRentChannelFeeRequest, RentalOption, rental_option};

let request = tonic::Request::new(EstimateRentChannelFeeRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    asset_id: "BTC".to_string(),
    lifetime_seconds: 2592000,
    amount: "10000000".to_string(),
    rental_option: Some(RentalOption {
        option: Some(rental_option::Option::DualFund(
            rental_option::DualFundOption {
                self_amount: "1000000".to_string(), // Your 0.01 BTC contribution
            }
        )),
    }),
});

let response = client.estimate_rent_channel_fee(request).await?;
println!("Rental fee: {}", response.into_inner().fee);

Example Response:

{
  "fee": "10000"
}

Rent Channel

Execute a channel rental to receive inbound liquidity.

Method: RentChannel

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork to rent on
asset_idstringYESAsset to rent
lifetime_secondsuint64YESRental lifetime in seconds
amountDecimalStringYESAmount of inbound liquidity
rental_optionRentalOptionYESPayment or DualFund option

Response:

FieldTypeDescription
rental_txidstringRental channel funding transaction ID
rented_channel_idstringID of the rented channel
rental_paymentRentalPaymentPayment details

RentalPayment (one of):

Onchain Payment

FieldTypeDescription
payment_txidstringPayment transaction ID
deposit_addressstringDeposit address for payment
fee_amountDecimalStringFee amount paid
metadatabytesPayment metadata

Offchain Payment

FieldTypeDescription
payment_idstringLightning payment ID
payment_requeststringLightning invoice (if applicable)

Example Request (OFFCHAIN):

TypeScript
const request = new RentChannelRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setAssetId('BTC')
request.setLifetimeSeconds(2592000) // 30 days
request.setAmount('10000000') // 0.1 BTC

request.setRentalOption({
  payment: {
    paymentNetwork: { protocol: 0, chainId: '0', name: 'Bitcoin' },
    paymentAssetId: 'BTC',
    paymentMethod: 1 // OFFCHAIN
  }
})

const response = await client.rentChannel(request, {})
console.log('Rented channel ID:', response.getRentedChannelId())
console.log('Rental TX:', response.getRentalTxid())

const payment = response.getRentalPayment()
if (payment.hasOffchain()) {
  console.log('Payment ID:', payment.getOffchain()?.getPaymentId())
}
Go
req := &pb.RentChannelRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    AssetId:         "BTC",
    LifetimeSeconds: 2592000,   // 30 days
    Amount:          "10000000", // 0.1 BTC
    RentalOption: &pb.RentalOption{
        Option: &pb.RentalOption_Payment{
            Payment: &pb.RentalOption_PaymentOption{
                PaymentNetwork: &pb.Network{
                    Protocol: 0,
                    ChainId:  "0",
                    Name:     "Bitcoin",
                },
                PaymentAssetId: "BTC",
                PaymentMethod:  pb.PaymentMethod_OFFCHAIN,
            },
        },
    },
}

resp, err := client.RentChannel(context.Background(), req)
if err != nil {
    log.Fatalf("RentChannel failed: %v", err)
}

fmt.Printf("Rented channel ID: %s\n", resp.RentedChannelId)
fmt.Printf("Rental TX: %s\n", resp.RentalTxid)

if offchain := resp.RentalPayment.GetOffchain(); offchain != nil {
    fmt.Printf("Payment ID: %s\n", offchain.PaymentId)
}
Rust
use hydra_api::{RentChannelRequest, RentalOption, rental_option, PaymentMethod};

let request = tonic::Request::new(RentChannelRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    asset_id: "BTC".to_string(),
    lifetime_seconds: 2592000,       // 30 days
    amount: "10000000".to_string(),  // 0.1 BTC
    rental_option: Some(RentalOption {
        option: Some(rental_option::Option::Payment(
            rental_option::PaymentOption {
                payment_network: Some(Network {
                    protocol: 0,
                    chain_id: "0".to_string(),
                    name: "Bitcoin".to_string(),
                }),
                payment_asset_id: "BTC".to_string(),
                payment_method: PaymentMethod::Offchain as i32,
                ..Default::default()
            }
        )),
    }),
});

let response = client.rent_channel(request).await?;
let rental = response.into_inner();

println!("Rented channel ID: {}", rental.rented_channel_id);
println!("Rental TX: {}", rental.rental_txid);

if let Some(payment) = rental.rental_payment {
    if let Some(offchain) = payment.offchain {
        println!("Payment ID: {}", offchain.payment_id);
    }
}

Example Request (DualFund):

TypeScript
const request = new RentChannelRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setAssetId('BTC')
request.setLifetimeSeconds(2592000)
request.setAmount('10000000')

request.setRentalOption({
  dualFund: {
    selfAmount: '1000000' // Your contribution
  }
})

const response = await client.rentChannel(request, {})
console.log('Dual-funded channel ID:', response.getRentedChannelId())
console.log('Funding TX:', response.getRentalTxid())
Go
req := &pb.RentChannelRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    AssetId:         "BTC",
    LifetimeSeconds: 2592000,
    Amount:          "10000000",
    RentalOption: &pb.RentalOption{
        Option: &pb.RentalOption_DualFund{
            DualFund: &pb.RentalOption_DualFundOption{
                SelfAmount: "1000000", // Your contribution
            },
        },
    },
}

resp, err := client.RentChannel(context.Background(), req)
if err != nil {
    log.Fatalf("RentChannel failed: %v", err)
}

fmt.Printf("Dual-funded channel ID: %s\n", resp.RentedChannelId)
fmt.Printf("Funding TX: %s\n", resp.RentalTxid)
Rust
use hydra_api::{RentChannelRequest, RentalOption, rental_option};

let request = tonic::Request::new(RentChannelRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    asset_id: "BTC".to_string(),
    lifetime_seconds: 2592000,
    amount: "10000000".to_string(),
    rental_option: Some(RentalOption {
        option: Some(rental_option::Option::DualFund(
            rental_option::DualFundOption {
                self_amount: "1000000".to_string(), // Your contribution
            }
        )),
    }),
});

let response = client.rent_channel(request).await?;
let rental = response.into_inner();

println!("Dual-funded channel ID: {}", rental.rented_channel_id);
println!("Funding TX: {}", rental.rental_txid);

Example Response:

{
  "rental_txid": "abc123...",
  "rented_channel_id": "ch_def456",
  "rental_payment": {
    "offchain": {
      "payment_id": "payment_xyz789",
      "payment_request": ""
    }
  }
}

Rental Duration Guide

Duration is specified in seconds.

Common Durations:

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

Calculation Formula:

TypeScript
const lifetimeSeconds = days * 24 * 60 * 60

// Examples
const oneDay = 1 * 24 * 60 * 60       // 86,400
const oneWeek = 7 * 24 * 60 * 60      // 604,800
const thirtyDays = 30 * 24 * 60 * 60  // 2,592,000
Go
lifetimeSeconds := days * 24 * 60 * 60

// Examples
oneDay := 1 * 24 * 60 * 60      // 86,400
oneWeek := 7 * 24 * 60 * 60     // 604,800
thirtyDays := 30 * 24 * 60 * 60 // 2,592,000
Rust
let lifetime_seconds = days * 24 * 60 * 60;

// Examples
let one_day = 1 * 24 * 60 * 60;      // 86,400
let one_week = 7 * 24 * 60 * 60;     // 604,800
let thirty_days = 30 * 24 * 60 * 60; // 2,592,000

Payment Methods

⚠️ Currently, only OFFCHAIN payment is supported.

OFFCHAIN Payment (✅ Supported)

  • How it works: Fee is paid via Lightning payment
  • Pros: Instant, lower fees
  • Cons: Requires existing offchain balance
  • Use when: You have Lightning channels with sufficient balance

DualFund Option (✅ Supported)

  • How it works: You and the provider both contribute to the channel
  • Pros: Efficient use of capital, shared funding
  • Use when: You want to contribute some funds to the rental channel

ONCHAIN Payment (⚠️ Not Currently Supported)

  • Status: Not yet implemented
  • Future availability: Will be added in a future release

Common Workflows

Rent a channel with OFFCHAIN payment

TypeScript
async function rentChannelOffchain(
  client: RentalServiceClient,
  network: Network,
  assetId: string,
  amount: string,
  durationDays: number
) {
  // 1. Check availability
  const infoReq = new GetRentableAssetInfoRequest()
  infoReq.setNetwork(network)
  infoReq.setAssetId(assetId)

  const info = await client.getRentableAssetInfo(infoReq, {})

  if (BigInt(amount) > BigInt(info.getAvailableLiquidity())) {
    throw new Error('Insufficient liquidity available')
  }

  // 2. Calculate duration in seconds
  const lifetimeSeconds = durationDays * 24 * 60 * 60

  // 3. Estimate fee
  const estimateReq = new EstimateRentChannelFeeRequest()
  estimateReq.setNetwork(network)
  estimateReq.setAssetId(assetId)
  estimateReq.setLifetimeSeconds(lifetimeSeconds)
  estimateReq.setAmount(amount)
  estimateReq.setRentalOption({
    payment: {
      paymentNetwork: network,
      paymentAssetId: assetId,
      paymentMethod: 1 // OFFCHAIN
    }
  })

  const estimate = await client.estimateRentChannelFee(estimateReq, {})
  console.log('Rental fee:', estimate.getFee())

  // 4. Execute rental
  const rentReq = new RentChannelRequest()
  rentReq.setNetwork(network)
  rentReq.setAssetId(assetId)
  rentReq.setLifetimeSeconds(lifetimeSeconds)
  rentReq.setAmount(amount)
  rentReq.setRentalOption({
    payment: {
      paymentNetwork: network,
      paymentAssetId: assetId,
      paymentMethod: 1 // OFFCHAIN
    }
  })

  const result = await client.rentChannel(rentReq, {})

  return {
    channelId: result.getRentedChannelId(),
    txid: result.getRentalTxid(),
    paymentId: result.getRentalPayment()?.getOffchain()?.getPaymentId()
  }
}

// Usage: Rent 0.1 BTC for 30 days
const rental = await rentChannelOffchain(
  client,
  { protocol: 0, chainId: '0', name: 'Bitcoin' },
  'BTC',
  '10000000',  // 0.1 BTC
  30           // 30 days
)

console.log('Channel rented:', rental.channelId)
Go
func rentChannelOffchain(
    client pb.RentalServiceClient,
    network *pb.Network,
    assetId string,
    amount string,
    durationDays int,
) (map[string]string, error) {
    // 1. Check availability
    infoReq := &pb.GetRentableAssetInfoRequest{
        Network: network,
        AssetId: assetId,
    }

    info, err := client.GetRentableAssetInfo(context.Background(), infoReq)
    if err != nil {
        return nil, err
    }

    amountBig, _ := new(big.Int).SetString(amount, 10)
    availBig, _ := new(big.Int).SetString(info.AvailableLiquidity, 10)
    if amountBig.Cmp(availBig) > 0 {
        return nil, fmt.Errorf("insufficient liquidity available")
    }

    // 2. Calculate duration in seconds
    lifetimeSeconds := uint64(durationDays * 24 * 60 * 60)

    // 3. Estimate fee
    estimateReq := &pb.EstimateRentChannelFeeRequest{
        Network:         network,
        AssetId:         assetId,
        LifetimeSeconds: lifetimeSeconds,
        Amount:          amount,
        RentalOption: &pb.RentalOption{
            Option: &pb.RentalOption_Payment{
                Payment: &pb.RentalOption_PaymentOption{
                    PaymentNetwork: network,
                    PaymentAssetId: assetId,
                    PaymentMethod:  pb.PaymentMethod_OFFCHAIN,
                },
            },
        },
    }

    estimate, err := client.EstimateRentChannelFee(context.Background(), estimateReq)
    if err != nil {
        return nil, err
    }
    fmt.Printf("Rental fee: %s\n", estimate.Fee)

    // 4. Execute rental
    rentReq := &pb.RentChannelRequest{
        Network:         network,
        AssetId:         assetId,
        LifetimeSeconds: lifetimeSeconds,
        Amount:          amount,
        RentalOption: &pb.RentalOption{
            Option: &pb.RentalOption_Payment{
                Payment: &pb.RentalOption_PaymentOption{
                    PaymentNetwork: network,
                    PaymentAssetId: assetId,
                    PaymentMethod:  pb.PaymentMethod_OFFCHAIN,
                },
            },
        },
    }

    result, err := client.RentChannel(context.Background(), rentReq)
    if err != nil {
        return nil, err
    }

    return map[string]string{
        "channelId": result.RentedChannelId,
        "txid":      result.RentalTxid,
        "paymentId": result.RentalPayment.GetOffchain().GetPaymentId(),
    }, nil
}

// Usage: Rent 0.1 BTC for 30 days
rental, err := rentChannelOffchain(
    client,
    &pb.Network{Protocol: 0, ChainId: "0", Name: "Bitcoin"},
    "BTC",
    "10000000", // 0.1 BTC
    30,         // 30 days
)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Channel rented: %s\n", rental["channelId"])
Rust
use hydra_api::{RentalServiceClient, GetRentableAssetInfoRequest, EstimateRentChannelFeeRequest, RentChannelRequest, Network, RentalOption, rental_option, PaymentMethod};
use std::collections::HashMap;

async fn rent_channel_offchain(
    client: &mut RentalServiceClient<Channel>,
    network: Network,
    asset_id: String,
    amount: String,
    duration_days: u64,
) -> Result<HashMap<String, String>, Box<dyn std::error::Error>> {
    // 1. Check availability
    let info_req = tonic::Request::new(GetRentableAssetInfoRequest {
        network: Some(network.clone()),
        asset_id: asset_id.clone(),
    });

    let info = client.get_rentable_asset_info(info_req).await?.into_inner();

    let amount_big: u64 = amount.parse()?;
    let avail_big: u64 = info.available_liquidity.parse()?;
    if amount_big > avail_big {
        return Err("Insufficient liquidity available".into());
    }

    // 2. Calculate duration in seconds
    let lifetime_seconds = duration_days * 24 * 60 * 60;

    // 3. Estimate fee
    let estimate_req = tonic::Request::new(EstimateRentChannelFeeRequest {
        network: Some(network.clone()),
        asset_id: asset_id.clone(),
        lifetime_seconds,
        amount: amount.clone(),
        rental_option: Some(RentalOption {
            option: Some(rental_option::Option::Payment(
                rental_option::PaymentOption {
                    payment_network: Some(network.clone()),
                    payment_asset_id: asset_id.clone(),
                    payment_method: PaymentMethod::Offchain as i32,
                    ..Default::default()
                }
            )),
        }),
    });

    let estimate = client.estimate_rent_channel_fee(estimate_req).await?.into_inner();
    println!("Rental fee: {}", estimate.fee);

    // 4. Execute rental
    let rent_req = tonic::Request::new(RentChannelRequest {
        network: Some(network.clone()),
        asset_id: asset_id.clone(),
        lifetime_seconds,
        amount: amount.clone(),
        rental_option: Some(RentalOption {
            option: Some(rental_option::Option::Payment(
                rental_option::PaymentOption {
                    payment_network: Some(network),
                    payment_asset_id: asset_id,
                    payment_method: PaymentMethod::Offchain as i32,
                    ..Default::default()
                }
            )),
        }),
    });

    let result = client.rent_channel(rent_req).await?.into_inner();

    let mut rental = HashMap::new();
    rental.insert("channelId".to_string(), result.rented_channel_id);
    rental.insert("txid".to_string(), result.rental_txid);
    if let Some(payment) = result.rental_payment {
        if let Some(offchain) = payment.offchain {
            rental.insert("paymentId".to_string(), offchain.payment_id);
        }
    }

    Ok(rental)
}

// Usage: Rent 0.1 BTC for 30 days
let rental = rent_channel_offchain(
    &mut client,
    Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    },
    "BTC".to_string(),
    "10000000".to_string(), // 0.1 BTC
    30,                      // 30 days
).await?;

println!("Channel rented: {}", rental.get("channelId").unwrap());

Rent with dual-funded channel

TypeScript
async function rentChannelDualFund(
  client: RentalServiceClient,
  network: Network,
  assetId: string,
  amount: string,
  selfContribution: string,
  durationDays: number
) {
  const lifetimeSeconds = durationDays * 24 * 60 * 60

  // 1. Estimate fee
  const estimateReq = new EstimateRentChannelFeeRequest()
  estimateReq.setNetwork(network)
  estimateReq.setAssetId(assetId)
  estimateReq.setLifetimeSeconds(lifetimeSeconds)
  estimateReq.setAmount(amount)
  estimateReq.setRentalOption({
    dualFund: {
      selfAmount: selfContribution
    }
  })

  const estimate = await client.estimateRentChannelFee(estimateReq, {})
  console.log('Rental fee:', estimate.getFee())

  // 2. Execute rental
  const rentReq = new RentChannelRequest()
  rentReq.setNetwork(network)
  rentReq.setAssetId(assetId)
  rentReq.setLifetimeSeconds(lifetimeSeconds)
  rentReq.setAmount(amount)
  rentReq.setRentalOption({
    dualFund: {
      selfAmount: selfContribution
    }
  })

  const result = await client.rentChannel(rentReq, {})

  return {
    channelId: result.getRentedChannelId(),
    txid: result.getRentalTxid()
  }
}

// Usage: Rent 0.1 BTC, contribute 0.01 BTC yourself
const rental = await rentChannelDualFund(
  client,
  { protocol: 0, chainId: '0', name: 'Bitcoin' },
  'BTC',
  '10000000',   // 0.1 BTC total inbound
  '1000000',    // 0.01 BTC your contribution
  30            // 30 days
)
Go
func rentChannelDualFund(
    client pb.RentalServiceClient,
    network *pb.Network,
    assetId string,
    amount string,
    selfContribution string,
    durationDays int,
) (map[string]string, error) {
    lifetimeSeconds := uint64(durationDays * 24 * 60 * 60)

    // 1. Estimate fee
    estimateReq := &pb.EstimateRentChannelFeeRequest{
        Network:         network,
        AssetId:         assetId,
        LifetimeSeconds: lifetimeSeconds,
        Amount:          amount,
        RentalOption: &pb.RentalOption{
            Option: &pb.RentalOption_DualFund{
                DualFund: &pb.RentalOption_DualFundOption{
                    SelfAmount: selfContribution,
                },
            },
        },
    }

    estimate, err := client.EstimateRentChannelFee(context.Background(), estimateReq)
    if err != nil {
        return nil, err
    }
    fmt.Printf("Rental fee: %s\n", estimate.Fee)

    // 2. Execute rental
    rentReq := &pb.RentChannelRequest{
        Network:         network,
        AssetId:         assetId,
        LifetimeSeconds: lifetimeSeconds,
        Amount:          amount,
        RentalOption: &pb.RentalOption{
            Option: &pb.RentalOption_DualFund{
                DualFund: &pb.RentalOption_DualFundOption{
                    SelfAmount: selfContribution,
                },
            },
        },
    }

    result, err := client.RentChannel(context.Background(), rentReq)
    if err != nil {
        return nil, err
    }

    return map[string]string{
        "channelId": result.RentedChannelId,
        "txid":      result.RentalTxid,
    }, nil
}

// Usage: Rent 0.1 BTC, contribute 0.01 BTC yourself
rental, err := rentChannelDualFund(
    client,
    &pb.Network{Protocol: 0, ChainId: "0", Name: "Bitcoin"},
    "BTC",
    "10000000", // 0.1 BTC total inbound
    "1000000",  // 0.01 BTC your contribution
    30,         // 30 days
)
if err != nil {
    log.Fatal(err)
}
Rust
use hydra_api::{RentalServiceClient, EstimateRentChannelFeeRequest, RentChannelRequest, Network, RentalOption, rental_option};
use std::collections::HashMap;

async fn rent_channel_dual_fund(
    client: &mut RentalServiceClient<Channel>,
    network: Network,
    asset_id: String,
    amount: String,
    self_contribution: String,
    duration_days: u64,
) -> Result<HashMap<String, String>, Box<dyn std::error::Error>> {
    let lifetime_seconds = duration_days * 24 * 60 * 60;

    // 1. Estimate fee
    let estimate_req = tonic::Request::new(EstimateRentChannelFeeRequest {
        network: Some(network.clone()),
        asset_id: asset_id.clone(),
        lifetime_seconds,
        amount: amount.clone(),
        rental_option: Some(RentalOption {
            option: Some(rental_option::Option::DualFund(
                rental_option::DualFundOption {
                    self_amount: self_contribution.clone(),
                }
            )),
        }),
    });

    let estimate = client.estimate_rent_channel_fee(estimate_req).await?.into_inner();
    println!("Rental fee: {}", estimate.fee);

    // 2. Execute rental
    let rent_req = tonic::Request::new(RentChannelRequest {
        network: Some(network),
        asset_id,
        lifetime_seconds,
        amount,
        rental_option: Some(RentalOption {
            option: Some(rental_option::Option::DualFund(
                rental_option::DualFundOption {
                    self_amount: self_contribution,
                }
            )),
        }),
    });

    let result = client.rent_channel(rent_req).await?.into_inner();

    let mut rental = HashMap::new();
    rental.insert("channelId".to_string(), result.rented_channel_id);
    rental.insert("txid".to_string(), result.rental_txid);

    Ok(rental)
}

// Usage: Rent 0.1 BTC, contribute 0.01 BTC yourself
let rental = rent_channel_dual_fund(
    &mut client,
    Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    },
    "BTC".to_string(),
    "10000000".to_string(),  // 0.1 BTC total inbound
    "1000000".to_string(),   // 0.01 BTC your contribution
    30,                       // 30 days
).await?;

Calculate rental cost

TypeScript
function calculateRentalCost(
  amount: string,
  feeRatio: string
): string {
  const amountBigInt = BigInt(amount)
  const feeRatioBigInt = BigInt(parseFloat(feeRatio) * 1000000)
  const rentalFee = (amountBigInt * feeRatioBigInt) / BigInt(1000000)

  return rentalFee.toString()
}

// Example: 0.1 BTC at 0.1% fee
const fee = calculateRentalCost('10000000', '0.001')
console.log('Rental fee:', fee, 'satoshis') // 10000 satoshis
Go
import "math/big"

func calculateRentalCost(amount string, feeRatio string) (string, error) {
    amountBig, ok := new(big.Int).SetString(amount, 10)
    if !ok {
        return "", fmt.Errorf("invalid amount")
    }

    feeRatioFloat, err := strconv.ParseFloat(feeRatio, 64)
    if err != nil {
        return "", err
    }

    feeRatioBig := big.NewInt(int64(feeRatioFloat * 1000000))
    rentalFee := new(big.Int).Mul(amountBig, feeRatioBig)
    rentalFee.Div(rentalFee, big.NewInt(1000000))

    return rentalFee.String(), nil
}

// Example: 0.1 BTC at 0.1% fee
fee, err := calculateRentalCost("10000000", "0.001")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Rental fee: %s satoshis\n", fee) // 10000 satoshis
Rust
fn calculate_rental_cost(amount: &str, fee_ratio: &str) -> Result<String, Box<dyn std::error::Error>> {
    let amount_u64: u64 = amount.parse()?;
    let fee_ratio_f64: f64 = fee_ratio.parse()?;

    let fee_ratio_scaled = (fee_ratio_f64 * 1_000_000.0) as u64;
    let rental_fee = (amount_u64 * fee_ratio_scaled) / 1_000_000;

    Ok(rental_fee.to_string())
}

// Example: 0.1 BTC at 0.1% fee
let fee = calculate_rental_cost("10000000", "0.001")?;
println!("Rental fee: {} satoshis", fee); // 10000 satoshis

Error Handling

Error CodeDescriptionSolution
INVALID_ARGUMENTInvalid asset_id or parametersCheck asset ID format and parameter ranges
RESOURCE_EXHAUSTEDInsufficient liquidity availableReduce amount or try different asset
FAILED_PRECONDITIONDuration outside allowed rangeCheck min/max duration from GetRentableAssetInfo
UNAVAILABLERental service temporarily unavailableRetry with exponential backoff

Best Practices

  1. Always estimate first - Call EstimateRentChannelFee before RentChannel
  2. Check availability - Verify available_liquidity before renting large amounts
  3. Choose appropriate duration - Longer rentals have better fee efficiency
  4. Use OFFCHAIN when possible - Cheaper and faster than ONCHAIN (when available)
  5. Consider dual-funding - Efficient use of capital if you have funds
  6. Monitor channel status - Use Node API to track channel becoming active
  7. Plan for expiry - Rental channels close after lifetime expires

Fee Calculation Example

TypeScript
// Asset: BTC
// Amount: 10,000,000 satoshis (0.1 BTC)
// Lifetime: 2,592,000 seconds (30 days)
// Fee Ratio: 0.001 (0.1%)
// Payment: OFFCHAIN

const rentalFee = 10_000_000 * 0.001 = 10_000 sats
const totalCost = 10_000 sats (~$10.00 at $100k BTC)

// Effective rate: 0.1% for 30 days = 1.2% annualized
Go
// Asset: BTC
// Amount: 10,000,000 satoshis (0.1 BTC)
// Lifetime: 2,592,000 seconds (30 days)
// Fee Ratio: 0.001 (0.1%)
// Payment: OFFCHAIN

rentalFee := 10_000_000 * 0.001 // 10_000 sats
totalCost := 10_000 // sats (~$10.00 at $100k BTC)

// Effective rate: 0.1% for 30 days = 1.2% annualized
Rust
// Asset: BTC
// Amount: 10,000,000 satoshis (0.1 BTC)
// Lifetime: 2,592,000 seconds (30 days)
// Fee Ratio: 0.001 (0.1%)
// Payment: OFFCHAIN

let rental_fee = 10_000_000.0 * 0.001; // 10_000 sats
let total_cost = 10_000; // sats (~$10.00 at $100k BTC)

// Effective rate: 0.1% for 30 days = 1.2% annualized

RentalOption Selection Guide

Choose the appropriate rental option based on your situation:

SituationRental OptionWhy
Have Lightning balancePayment (OFFCHAIN)Fast, low fees
Want to contribute fundsDualFundEfficient capital use
No Lightning balanceNot currently supportedONCHAIN coming soon

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


Copyright © 2025