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:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Network to query balances for |
Network Object:
| Field | Type | Description |
|---|---|---|
protocol | int32 | Network protocol (0=Bitcoin, 1=EVM) |
chain_id | string | Chain identifier |
name | string | Human-readable network name |
Response:
| Field | Type | Description |
|---|---|---|
balances | map<string, Balance> | Map of asset_id → Balance |
Balance Object:
| Field | Type | Description |
|---|---|---|
onchain | OnchainBalance | Onchain balance details |
offchain | OffchainBalance | Offchain (Lightning) balance details |
OnchainBalance:
| Field | Type | Description |
|---|---|---|
confirmed | DecimalString | Confirmed balance |
unconfirmed | DecimalString | Unconfirmed balance |
reserved | DecimalString | Reserved for channel operations |
OffchainBalance:
| Field | Type | Description |
|---|---|---|
local | DecimalString | Balance you can send |
remote | DecimalString | Balance 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:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Network to query |
asset_id | string | YES | Asset identifier (e.g., "BTC", "0x...") |
Response:
| Field | Type | Description |
|---|---|---|
balance | Balance | Balance 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:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Network to query transactions for |
Response:
| Field | Type | Description |
|---|---|---|
transactions | Transaction[] | Array of transactions |
Transaction Object:
| Field | Type | Description |
|---|---|---|
txid | string | Transaction ID |
timestamp | Timestamp | Transaction timestamp |
confirmations | uint32 | Number of confirmations |
asset_transfers | AssetTransfer[] | 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:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Network where transaction occurred |
txid | string | YES | Transaction ID |
Response:
| Field | Type | Description |
|---|---|---|
transaction | Transaction | Transaction 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 Code | Description | Solution |
|---|---|---|
INVALID_ARGUMENT | Invalid network or asset_id | Check asset ID format for the network |
NOT_FOUND | Transaction not found | Verify txid is correct |
UNAVAILABLE | Service temporarily unavailable | Retry with exponential backoff |
Best Practices
- Cache balance queries - Balances don't change instantly, cache for 10-30 seconds
- Use GetBalances for multiple assets - More efficient than multiple GetBalance calls
- Check both onchain and offchain - Lightning payments require offchain balance
- Monitor reserved balance - Reserved funds cannot be spent until channel operations complete
- Handle unconfirmed balance - Don't rely on unconfirmed funds for critical operations