Api

Wallet API

Balance and transaction management

The Wallet API provides access to balance information and transaction history for all supported networks.

Endpoints


Get Balances

Get all asset balances for a specific network.

Method: GetBalances

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork to query balances for

Network Object:

FieldTypeDescription
protocolint32Network protocol (0=Bitcoin, 1=EVM)
chain_idstringChain identifier
namestringHuman-readable network name

Response:

FieldTypeDescription
balancesmap<string, Balance>Map of asset_id → Balance

Balance Object:

FieldTypeDescription
onchainOnchainBalanceOnchain balance details
offchainOffchainBalanceOffchain (Lightning) balance details

OnchainBalance:

FieldTypeDescription
confirmedDecimalStringConfirmed balance
unconfirmedDecimalStringUnconfirmed balance
reservedDecimalStringReserved for channel operations

OffchainBalance:

FieldTypeDescription
localDecimalStringBalance you can send
remoteDecimalStringBalance you can receive

Example Request:

TypeScript
import { WalletServiceClient } from './proto/WalletServiceClientPb'
import { GetBalancesRequest } from './proto/wallet_pb'

const client = new WalletServiceClient('http://localhost:5001')

const request = new GetBalancesRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})

const response = await client.getBalances(request, {})
const balances = response.getBalancesMap()
Go
import (
    pb "github.com/hydra/hydra-go/proto"
)

client := pb.NewWalletServiceClient(conn)

req := &pb.GetBalancesRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
}

resp, err := client.GetBalances(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

balances := resp.Balances
Rust
use hydra_api::wallet_service_client::WalletServiceClient;
use hydra_api::{GetBalancesRequest, Network};

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

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

let response = client.get_balances(request).await?;
let balances = response.into_inner().balances;

Example Response:

{
  "balances": {
    "BTC": {
      "onchain": {
        "confirmed": "50000000",
        "unconfirmed": "0",
        "reserved": "5000000"
      },
      "offchain": {
        "local": "10000000",
        "remote": "5000000"
      }
    },
    "RGB:abc123": {
      "onchain": {
        "confirmed": "1000",
        "unconfirmed": "0",
        "reserved": "0"
      },
      "offchain": {
        "local": "500",
        "remote": "500"
      }
    }
  }
}

Get Balance

Get the balance of a specific asset on a network.

Method: GetBalance

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork to query
asset_idstringYESAsset identifier (e.g., "BTC", "0x...")

Response:

FieldTypeDescription
balanceBalanceBalance details for the asset

Example Request:

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

const response = await client.getBalance(request, {})
const balance = response.getBalance()
Go
req := &pb.GetBalanceRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    AssetId: "BTC",
}

resp, err := client.GetBalance(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

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

let response = client.get_balance(request).await?;
let balance = response.into_inner().balance;

Example Response:

{
  "balance": {
    "onchain": {
      "confirmed": "50000000",
      "unconfirmed": "0",
      "reserved": "5000000"
    },
    "offchain": {
      "local": "10000000",
      "remote": "5000000"
    }
  }
}

Get Transactions

Get all transactions for a network.

Method: GetTransactions

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork to query transactions for

Response:

FieldTypeDescription
transactionsTransaction[]Array of transactions

Transaction Object:

FieldTypeDescription
txidstringTransaction ID
timestampTimestampTransaction timestamp
confirmationsuint32Number of confirmations
asset_transfersAssetTransfer[]Asset movements in this transaction

Example Request:

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

const response = await client.getTransactions(request, {})
const transactions = response.getTransactionsList()
Go
req := &pb.GetTransactionsRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
}

resp, err := client.GetTransactions(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

transactions := resp.Transactions
Rust
let request = tonic::Request::new(GetTransactionsRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
});

let response = client.get_transactions(request).await?;
let transactions = response.into_inner().transactions;

Example Response:

{
  "transactions": [
    {
      "txid": "abc123...",
      "timestamp": "2025-10-03T10:30:00Z",
      "confirmations": 6,
      "asset_transfers": [
        {
          "asset_id": "BTC",
          "amount": "1000000",
          "direction": "OUTGOING"
        }
      ]
    },
    {
      "txid": "def456...",
      "timestamp": "2025-10-02T14:20:00Z",
      "confirmations": 144,
      "asset_transfers": [
        {
          "asset_id": "BTC",
          "amount": "5000000",
          "direction": "INCOMING"
        }
      ]
    }
  ]
}

Get Transaction

Get details of a specific transaction by its ID.

Method: GetTransaction

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork where transaction occurred
txidstringYESTransaction ID

Response:

FieldTypeDescription
transactionTransactionTransaction details

Example Request:

TypeScript
const request = new GetTransactionRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setTxid('abc123...')

const response = await client.getTransaction(request, {})
const transaction = response.getTransaction()
Go
req := &pb.GetTransactionRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    Txid: "abc123...",
}

resp, err := client.GetTransaction(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

transaction := resp.Transaction
Rust
let request = tonic::Request::new(GetTransactionRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    txid: "abc123...".to_string(),
});

let response = client.get_transaction(request).await?;
let transaction = response.into_inner().transaction;

Example Response:

{
  "transaction": {
    "txid": "abc123...",
    "timestamp": "2025-10-03T10:30:00Z",
    "confirmations": 6,
    "fee": "2500",
    "asset_transfers": [
      {
        "asset_id": "BTC",
        "amount": "1000000",
        "direction": "OUTGOING"
      }
    ]
  }
}

Asset ID Formats

Different networks use different asset ID formats:

Bitcoin

  • Native: "BTC"
  • RGB Assets: "RGB:contract_id"

Ethereum/EVM

  • Native: "0x0000000000000000000000000000000000000000" (42-character zero address)
  • ERC20 Tokens: "ERC20:0x..." (contract address)

Common Patterns

Check if user has sufficient balance

TypeScript
async function hasSufficientBalance(
  client: WalletServiceClient,
  network: Network,
  assetId: string,
  requiredAmount: string,
  balanceType: 'onchain' | 'offchain'
): Promise<boolean> {
  const request = new GetBalanceRequest()
  request.setNetwork(network)
  request.setAssetId(assetId)

  const response = await client.getBalance(request, {})
  const balance = response.getBalance()

  const available = balanceType === 'onchain'
    ? balance.getOnchain()?.getConfirmed()
    : balance.getOffchain()?.getLocal()

  return BigInt(available || '0') >= BigInt(requiredAmount)
}
Go
func hasSufficientBalance(
    client pb.WalletServiceClient,
    network *pb.Network,
    assetId string,
    requiredAmount string,
    balanceType string,
) (bool, error) {
    req := &pb.GetBalanceRequest{
        Network: network,
        AssetId: assetId,
    }

    resp, err := client.GetBalance(context.Background(), req)
    if err != nil {
        return false, err
    }

    balance := resp.Balance
    var available string

    if balanceType == "onchain" {
        available = balance.Onchain.Confirmed
    } else {
        available = balance.Offchain.Local
    }

    requiredBig := new(big.Int)
    requiredBig.SetString(requiredAmount, 10)

    availableBig := new(big.Int)
    availableBig.SetString(available, 10)

    return availableBig.Cmp(requiredBig) >= 0, nil
}
Rust
async fn has_sufficient_balance(
    client: &mut WalletServiceClient<Channel>,
    network: Network,
    asset_id: String,
    required_amount: String,
    balance_type: &str,
) -> Result<bool, Box<dyn std::error::Error>> {
    let request = tonic::Request::new(GetBalanceRequest {
        network: Some(network),
        asset_id,
    });

    let response = client.get_balance(request).await?;
    let balance = response.into_inner().balance.unwrap();

    let available = if balance_type == "onchain" {
        balance.onchain.unwrap().confirmed
    } else {
        balance.offchain.unwrap().local
    };

    let required: u64 = required_amount.parse()?;
    let available_amount: u64 = available.parse()?;

    Ok(available_amount >= required)
}

Monitor transaction confirmations

TypeScript
async function waitForConfirmations(
  client: WalletServiceClient,
  network: Network,
  txid: string,
  requiredConfirmations: number
): Promise<void> {
  while (true) {
    const request = new GetTransactionRequest()
    request.setNetwork(network)
    request.setTxid(txid)

    const response = await client.getTransaction(request, {})
    const tx = response.getTransaction()

    if (tx.getConfirmations() >= requiredConfirmations) {
      return
    }

    await new Promise(resolve => setTimeout(resolve, 30000)) // Wait 30s
  }
}
Go
func waitForConfirmations(
    client pb.WalletServiceClient,
    network *pb.Network,
    txid string,
    requiredConfirmations uint32,
) error {
    for {
        req := &pb.GetTransactionRequest{
            Network: network,
            Txid:    txid,
        }

        resp, err := client.GetTransaction(context.Background(), req)
        if err != nil {
            return err
        }

        tx := resp.Transaction

        if tx.Confirmations >= requiredConfirmations {
            return nil
        }

        time.Sleep(30 * time.Second) // Wait 30s
    }
}
Rust
async fn wait_for_confirmations(
    client: &mut WalletServiceClient<Channel>,
    network: Network,
    txid: String,
    required_confirmations: u32,
) -> Result<(), Box<dyn std::error::Error>> {
    loop {
        let request = tonic::Request::new(GetTransactionRequest {
            network: Some(network.clone()),
            txid: txid.clone(),
        });

        let response = client.get_transaction(request).await?;
        let tx = response.into_inner().transaction.unwrap();

        if tx.confirmations >= required_confirmations {
            return Ok(());
        }

        tokio::time::sleep(tokio::time::Duration::from_secs(30)).await; // Wait 30s
    }
}

Error Handling

Error CodeDescriptionSolution
INVALID_ARGUMENTInvalid network or asset_idCheck asset ID format for the network
NOT_FOUNDTransaction not foundVerify txid is correct
UNAVAILABLEService temporarily unavailableRetry with exponential backoff

Best Practices

  1. Cache balance queries - Balances don't change instantly, cache for 10-30 seconds
  2. Use GetBalances for multiple assets - More efficient than multiple GetBalance calls
  3. Check both onchain and offchain - Lightning payments require offchain balance
  4. Monitor reserved balance - Reserved funds cannot be spent until channel operations complete
  5. Handle unconfirmed balance - Don't rely on unconfirmed funds for critical operations

← Back to API Reference | Next: Pricing API →


Copyright © 2025