Client API
The Client API provides client-specific operations including getting deposit addresses and sending transactions.
Endpoints
Get Deposit Address
Get a deposit address for receiving funds on a specific network.
Service: ClientServiceMethod: GetDepositAddress
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Network to get deposit address for |
Network Object:
| Field | Type | Description |
|---|---|---|
protocol | int32 | Network protocol (0=Bitcoin, 1=EVM) |
id | string | Chain identifier (magic bytes for Bitcoin, chain ID for EVM) |
Response:
| Field | Type | Description |
|---|---|---|
address | string | Deposit address for the network |
Example Request:
TypeScript
import { hydraGrpcClient } from './services/grpcWebClient'
import { Protocol } from './proto/models'
// Get Bitcoin Signet deposit address
const address = await hydraGrpcClient.getDepositAddress({
protocol: Protocol.BITCOIN,
id: '0a03cf40' // Bitcoin Signet magic bytes
})
// Get Ethereum Sepolia deposit address
const ethAddress = await hydraGrpcClient.getDepositAddress({
protocol: Protocol.EVM,
id: '11155111' // Ethereum Sepolia chain ID
})
Go
import (
pb "github.com/hydra/hydra-go/proto"
)
client := pb.NewClientServiceClient(conn)
req := &pb.GetDepositAddressRequest{
Network: &pb.Network{
Protocol: pb.Protocol_BITCOIN,
Id: "0a03cf40",
},
}
resp, err := client.GetDepositAddress(context.Background(), req)
if err != nil {
log.Fatal(err)
}
address := resp.Address
Rust
use hydra_api::client_service_client::ClientServiceClient;
use hydra_api::{GetDepositAddressRequest, Network, Protocol};
let mut client = ClientServiceClient::new(channel);
let request = tonic::Request::new(GetDepositAddressRequest {
network: Some(Network {
protocol: Protocol::Bitcoin as i32,
id: "0a03cf40".to_string(),
}),
});
let response = client.get_deposit_address(request).await?;
let address = response.into_inner().address;
Example Response:
{
"address": "tb1q..."
}
Send Transaction
Send the native asset of a network to another address.
Service: ClientServiceMethod: SendTransaction
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Network to send transaction on |
to_address | string | YES | Recipient address |
amount | SendAmount | YES | Amount to send |
fee_option | FeeOption | YES | Fee option for the transaction |
SendAmount Object:
| Field | Type | Description |
|---|---|---|
value | DecimalString | Amount as decimal string |
asset_id | string | Asset identifier |
FeeOption Enum:
| Option | Description |
|---|---|
LOW | Low fee, slower confirmation |
MEDIUM | Medium fee, moderate confirmation time |
HIGH | High fee, faster confirmation |
CUSTOM | Custom fee rate (sats/vB for Bitcoin, gwei for EVM) |
Response:
| Field | Type | Description |
|---|---|---|
txid | string | Transaction ID |
Example Request:
TypeScript
import { FeeOption } from './proto/models'
const response = await hydraGrpcClient.sendTransaction({
network: {
protocol: Protocol.BITCOIN,
id: '0a03cf40'
},
toAddress: 'tb1q...',
amount: {
value: '0.001',
assetId: 'BTC'
},
feeOption: FeeOption.MEDIUM
})
console.log('Transaction ID:', response.txid)
Go
req := &pb.SendTransactionRequest{
Network: &pb.Network{
Protocol: pb.Protocol_BITCOIN,
Id: "0a03cf40",
},
ToAddress: "tb1q...",
Amount: &pb.SendAmount{
Value: "0.001",
AssetId: "BTC",
},
FeeOption: pb.FeeOption_MEDIUM,
}
resp, err := client.SendTransaction(context.Background(), req)
if err != nil {
log.Fatal(err)
}
txid := resp.Txid
Rust
let request = tonic::Request::new(SendTransactionRequest {
network: Some(Network {
protocol: Protocol::Bitcoin as i32,
id: "0a03cf40".to_string(),
}),
to_address: "tb1q...".to_string(),
amount: Some(SendAmount {
value: "0.001".to_string(),
asset_id: "BTC".to_string(),
}),
fee_option: FeeOption::Medium as i32,
});
let response = client.send_transaction(request).await?;
let txid = response.into_inner().txid;
Example Response:
{
"txid": "abc123..."
}
Send Token Transaction
Send a token (non-native asset) to another address on a network.
Service: ClientServiceMethod: SendTokenTransaction
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Network to send transaction on |
to_address | string | YES | Recipient address |
token_id | string | YES | Token contract address or asset ID |
amount | SendAmount | YES | Amount to send |
fee_option | FeeOption | YES | Fee option for the transaction |
Response:
| Field | Type | Description |
|---|---|---|
txid | string | Transaction ID |
Example Request:
TypeScript
// Send USDC on Ethereum Sepolia
const response = await hydraGrpcClient.sendTokenTransaction({
network: {
protocol: Protocol.EVM,
id: '11155111'
},
toAddress: '0x...',
tokenId: '0x...', // USDC contract address
amount: {
value: '100.0',
assetId: 'USDC'
},
feeOption: FeeOption.MEDIUM
})
Go
req := &pb.SendTokenTransactionRequest{
Network: &pb.Network{
Protocol: pb.Protocol_EVM,
Id: "11155111",
},
ToAddress: "0x...",
TokenId: "0x...",
Amount: &pb.SendAmount{
Value: "100.0",
AssetId: "USDC",
},
FeeOption: pb.FeeOption_MEDIUM,
}
resp, err := client.SendTokenTransaction(context.Background(), req)
Rust
let request = tonic::Request::new(SendTokenTransactionRequest {
network: Some(Network {
protocol: Protocol::Evm as i32,
id: "11155111".to_string(),
}),
to_address: "0x...".to_string(),
token_id: "0x...".to_string(),
amount: Some(SendAmount {
value: "100.0".to_string(),
asset_id: "USDC".to_string(),
}),
fee_option: FeeOption::Medium as i32,
});
let response = client.send_token_transaction(request).await?;
Example Response:
{
"txid": "0x..."
}
Bump Transaction
Increase the fee of an existing unconfirmed transaction to speed up confirmation.
Service: ClientServiceMethod: BumpTransaction
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Network where transaction exists |
txid | string | YES | Transaction ID to bump |
fee_option | FeeOption | YES | New fee option (must be higher than original) |
Response:
| Field | Type | Description |
|---|---|---|
txid | string | New transaction ID (or same if RBF) |
Example Request:
TypeScript
const response = await hydraGrpcClient.bumpTransaction({
network: {
protocol: Protocol.BITCOIN,
id: '0a03cf40'
},
txid: 'abc123...',
feeOption: FeeOption.HIGH
})
Go
req := &pb.BumpTransactionRequest{
Network: &pb.Network{
Protocol: pb.Protocol_BITCOIN,
Id: "0a03cf40",
},
Txid: "abc123...",
FeeOption: pb.FeeOption_HIGH,
}
resp, err := client.BumpTransaction(context.Background(), req)
Rust
let request = tonic::Request::new(BumpTransactionRequest {
network: Some(Network {
protocol: Protocol::Bitcoin as i32,
id: "0a03cf40".to_string(),
}),
txid: "abc123...".to_string(),
fee_option: FeeOption::High as i32,
});
let response = client.bump_transaction(request).await?;
Example Response:
{
"txid": "def456..."
}
Set Token Allowance
Set the allowance for a spender to use a certain amount of a token on behalf of the owner (EVM only).
Service: ClientServiceMethod: SetTokenAllowance
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | EVM network |
spender | string | YES | Spender address |
allowance | SetAllowance | YES | Allowance details |
fee_option | FeeOption | YES | Fee option for the transaction |
SetAllowance Object:
| Field | Type | Description |
|---|---|---|
token_id | string | Token contract address |
amount | DecimalString | Amount to allow |
Response:
| Field | Type | Description |
|---|---|---|
txid | string | Transaction ID |
Example Request:
TypeScript
const response = await hydraGrpcClient.setTokenAllowance({
network: {
protocol: Protocol.EVM,
id: '11155111'
},
spender: '0x...', // Contract or address to approve
allowance: {
tokenId: '0x...', // Token contract
amount: '1000.0'
},
feeOption: FeeOption.MEDIUM
})
Go
req := &pb.SetTokenAllowanceRequest{
Network: &pb.Network{
Protocol: pb.Protocol_EVM,
Id: "11155111",
},
Spender: "0x...",
Allowance: &pb.SetAllowance{
TokenId: "0x...",
Amount: "1000.0",
},
FeeOption: pb.FeeOption_MEDIUM,
}
resp, err := client.SetTokenAllowance(context.Background(), req)
Rust
let request = tonic::Request::new(SetTokenAllowanceRequest {
network: Some(Network {
protocol: Protocol::Evm as i32,
id: "11155111".to_string(),
}),
spender: "0x...".to_string(),
allowance: Some(SetAllowance {
token_id: "0x...".to_string(),
amount: "1000.0".to_string(),
}),
fee_option: FeeOption::Medium as i32,
});
let response = client.set_token_allowance(request).await?;
Example Response:
{
"txid": "0x..."
}
Network ID Formats
Different networks use different ID formats:
Bitcoin Networks
- Mainnet:
f9beb4d9 - Testnet3:
0b110907 - Testnet4:
1c163f28 - Signet:
0a03cf40 - Regtest:
fabfb5da
EVM Networks
- Ethereum Mainnet:
1 - Ethereum Sepolia:
11155111 - Arbitrum One:
42161 - Arbitrum Sepolia:
421614 - Optimism:
10 - Polygon:
137
Common Patterns
Get deposit address for all configured networks
TypeScript
async function getAllDepositAddresses() {
const networks = await hydraGrpcClient.getNetworks()
const addresses: Record<string, string> = {}
for (const network of networks) {
try {
const address = await hydraGrpcClient.getDepositAddress(network)
addresses[network.id] = address
} catch (error) {
console.error(`Failed to get address for ${network.id}:`, error)
}
}
return addresses
}
Go
func getAllDepositAddresses(client pb.ClientServiceClient) (map[string]string, error) {
networksResp, err := appClient.GetNetworks(context.Background(), &pb.GetNetworksRequest{})
if err != nil {
return nil, err
}
addresses := make(map[string]string)
for _, network := range networksResp.Networks {
resp, err := client.GetDepositAddress(context.Background(), &pb.GetDepositAddressRequest{
Network: network,
})
if err != nil {
log.Printf("Failed to get address for %s: %v", network.Id, err)
continue
}
addresses[network.Id] = resp.Address
}
return addresses, nil
}
Rust
async fn get_all_deposit_addresses(
client: &mut ClientServiceClient<Channel>,
app_client: &mut AppServiceClient<Channel>,
) -> Result<HashMap<String, String>, Box<dyn std::error::Error>> {
let networks = app_client
.get_networks(GetNetworksRequest {})
.await?
.into_inner()
.networks;
let mut addresses = HashMap::new();
for network in networks {
match client
.get_deposit_address(GetDepositAddressRequest {
network: Some(network.clone()),
})
.await
{
Ok(resp) => {
addresses.insert(network.id.clone(), resp.into_inner().address);
}
Err(e) => {
eprintln!("Failed to get address for {}: {}", network.id, e);
}
}
}
Ok(addresses)
}
Error Handling
| Error Code | Description | Solution |
|---|---|---|
INVALID_ARGUMENT | Invalid network, address, or amount | Verify all parameters are correctly formatted |
FAILED_PRECONDITION | Insufficient balance or wallet not initialized | Check balance and wallet status |
NOT_FOUND | Transaction not found (for BumpTransaction) | Verify transaction exists and is unconfirmed |
UNAVAILABLE | Service temporarily unavailable | Retry with exponential backoff |
Best Practices
- Validate addresses - Always validate recipient addresses before sending
- Check balances - Verify sufficient balance before sending transactions
- Use appropriate fees - Choose fee option based on urgency (LOW for non-urgent, HIGH for urgent)
- Cache deposit addresses - Deposit addresses don't change frequently
- Monitor transactions - Use the Wallet API to monitor transaction confirmations
- Handle errors gracefully - Network issues can occur, implement retry logic