Api
Pricing API
Asset price feeds
The Pricing API provides fiat price conversion for assets.
Endpoints
Get Asset Fiat Price
Get the current fiat price of an asset on a specific network.
Method: GetAssetFiatPrice
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Network where asset exists |
asset_id | string | YES | Asset identifier |
fiat_currency | FiatCurrency | YES | Target fiat currency |
FiatCurrency Enum:
| Value | Description |
|---|---|
USD | US Dollar |
EUR | Euro |
GBP | British Pound |
JPY | Japanese Yen |
CNY | Chinese Yuan |
AUD | Australian Dollar |
CAD | Canadian Dollar |
Response:
| Field | Type | Description |
|---|---|---|
price | DecimalString | Price in specified fiat currency |
Example Request:
Rust
use hydra_client::pricing::{PricingServiceClient, GetAssetFiatPriceRequest, FiatCurrency};
use hydra_client::common::Network;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut client = PricingServiceClient::connect("http://localhost:5001").await?;
let request = GetAssetFiatPriceRequest {
network: Some(Network {
protocol: 0,
chain_id: "0".to_string(),
name: "Bitcoin".to_string(),
}),
asset_id: "BTC".to_string(),
fiat_currency: FiatCurrency::Usd as i32,
};
let response = client.get_asset_fiat_price(request).await?;
let price = response.into_inner().price;
println!("BTC Price: ${}", price);
Ok(())
}
Go
package main
import (
"context"
"fmt"
"log"
pb "github.com/hydra/api/proto"
"google.golang.org/grpc"
)
func main() {
conn, err := grpc.Dial("localhost:5001", grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})))
if err != nil {
log.Fatalf("Failed to connect: %v", err)
}
defer conn.Close()
client := pb.NewPricingServiceClient(conn)
request := &pb.GetAssetFiatPriceRequest{
Network: &pb.Network{
Protocol: 0,
ChainId: "0",
Name: "Bitcoin",
},
AssetId: "BTC",
FiatCurrency: pb.FiatCurrency_USD,
}
response, err := client.GetAssetFiatPrice(context.Background(), request)
if err != nil {
log.Fatalf("Failed to get price: %v", err)
}
fmt.Printf("BTC Price: $%s\n", response.Price)
}
TypeScript
import { PricingServiceClient } from './proto/PricingServiceClientPb'
import { GetAssetFiatPriceRequest, FiatCurrency } from './proto/pricing_pb'
const client = new PricingServiceClient('http://localhost:5001')
const request = new GetAssetFiatPriceRequest()
request.setNetwork({
protocol: 0,
chainId: '0',
name: 'Bitcoin'
})
request.setAssetId('BTC')
request.setFiatCurrency(FiatCurrency.USD)
const response = await client.getAssetFiatPrice(request, {})
const price = response.getPrice()
console.log(`BTC Price: $${price}`)
Example Response:
{
"price": "98450.75"
}
Common Use Cases
Get multiple asset prices
Rust
use hydra_client::pricing::{PricingServiceClient, GetAssetFiatPriceRequest, FiatCurrency};
use hydra_client::common::Network;
use std::collections::HashMap;
async fn get_asset_prices(
client: &mut PricingServiceClient<tonic::transport::Channel>,
network: Network,
asset_ids: Vec<String>,
fiat_currency: FiatCurrency,
) -> Result<HashMap<String, String>, Box<dyn std::error::Error>> {
let mut prices = HashMap::new();
for asset_id in asset_ids {
let request = GetAssetFiatPriceRequest {
network: Some(network.clone()),
asset_id: asset_id.clone(),
fiat_currency: fiat_currency as i32,
};
let response = client.get_asset_fiat_price(request).await?;
prices.insert(asset_id, response.into_inner().price);
}
Ok(prices)
}
// Usage
let prices = get_asset_prices(
&mut client,
bitcoin_network,
vec!["BTC".to_string(), "RGB:asset123".to_string()],
FiatCurrency::Usd,
).await?;
println!("BTC: {}", prices.get("BTC").unwrap());
println!("RGB Asset: {}", prices.get("RGB:asset123").unwrap());
Go
package main
import (
"context"
"fmt"
pb "github.com/hydra/api/proto"
)
func getAssetPrices(
client pb.PricingServiceClient,
network *pb.Network,
assetIds []string,
fiatCurrency pb.FiatCurrency,
) (map[string]string, error) {
prices := make(map[string]string)
for _, assetId := range assetIds {
request := &pb.GetAssetFiatPriceRequest{
Network: network,
AssetId: assetId,
FiatCurrency: fiatCurrency,
}
response, err := client.GetAssetFiatPrice(context.Background(), request)
if err != nil {
return nil, err
}
prices[assetId] = response.Price
}
return prices, nil
}
// Usage
prices, err := getAssetPrices(
client,
bitcoinNetwork,
[]string{"BTC", "RGB:asset123"},
pb.FiatCurrency_USD,
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("BTC: %s\n", prices["BTC"])
fmt.Printf("RGB Asset: %s\n", prices["RGB:asset123"])
TypeScript
async function getAssetPrices(
client: PricingServiceClient,
network: Network,
assetIds: string[],
fiatCurrency: FiatCurrency
): Promise<Map<string, string>> {
const prices = new Map<string, string>()
for (const assetId of assetIds) {
const request = new GetAssetFiatPriceRequest()
request.setNetwork(network)
request.setAssetId(assetId)
request.setFiatCurrency(fiatCurrency)
const response = await client.getAssetFiatPrice(request, {})
prices.set(assetId, response.getPrice())
}
return prices
}
// Usage
const prices = await getAssetPrices(
client,
bitcoinNetwork,
['BTC', 'RGB:asset123'],
FiatCurrency.USD
)
console.log('BTC:', prices.get('BTC'))
console.log('RGB Asset:', prices.get('RGB:asset123'))
Convert crypto amount to fiat
Rust
use hydra_client::pricing::{PricingServiceClient, GetAssetFiatPriceRequest, FiatCurrency};
use hydra_client::common::Network;
async fn convert_to_fiat(
client: &mut PricingServiceClient<tonic::transport::Channel>,
network: Network,
asset_id: String,
amount: String,
fiat_currency: FiatCurrency,
) -> Result<String, Box<dyn std::error::Error>> {
let request = GetAssetFiatPriceRequest {
network: Some(network),
asset_id,
fiat_currency: fiat_currency as i32,
};
let response = client.get_asset_fiat_price(request).await?;
let price: f64 = response.into_inner().price.parse()?;
let asset_amount: f64 = amount.parse()?;
// Assuming amount is in base units (e.g., satoshis for BTC)
// Convert to main unit (BTC = 8 decimals)
let btc_amount = asset_amount / 100_000_000.0;
let fiat_value = btc_amount * price;
Ok(format!("{:.2}", fiat_value))
}
// Usage
let fiat_value = convert_to_fiat(
&mut client,
bitcoin_network,
"BTC".to_string(),
"100000000".to_string(), // 1 BTC in satoshis
FiatCurrency::Usd,
).await?;
println!("Value: ${}", fiat_value);
Go
package main
import (
"context"
"fmt"
"strconv"
pb "github.com/hydra/api/proto"
)
func convertToFiat(
client pb.PricingServiceClient,
network *pb.Network,
assetId string,
amount string,
fiatCurrency pb.FiatCurrency,
) (string, error) {
request := &pb.GetAssetFiatPriceRequest{
Network: network,
AssetId: assetId,
FiatCurrency: fiatCurrency,
}
response, err := client.GetAssetFiatPrice(context.Background(), request)
if err != nil {
return "", err
}
price, err := strconv.ParseFloat(response.Price, 64)
if err != nil {
return "", err
}
assetAmount, err := strconv.ParseFloat(amount, 64)
if err != nil {
return "", err
}
// Assuming amount is in base units (e.g., satoshis for BTC)
// Convert to main unit (BTC = 8 decimals)
btcAmount := assetAmount / 100000000
fiatValue := btcAmount * price
return fmt.Sprintf("%.2f", fiatValue), nil
}
// Usage
fiatValue, err := convertToFiat(
client,
bitcoinNetwork,
"BTC",
"100000000", // 1 BTC in satoshis
pb.FiatCurrency_USD,
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Value: $%s\n", fiatValue)
TypeScript
async function convertToFiat(
client: PricingServiceClient,
network: Network,
assetId: string,
amount: string,
fiatCurrency: FiatCurrency
): Promise<string> {
const request = new GetAssetFiatPriceRequest()
request.setNetwork(network)
request.setAssetId(assetId)
request.setFiatCurrency(fiatCurrency)
const response = await client.getAssetFiatPrice(request, {})
const price = parseFloat(response.getPrice())
const assetAmount = parseFloat(amount)
// Assuming amount is in base units (e.g., satoshis for BTC)
// Convert to main unit (BTC = 8 decimals)
const btcAmount = assetAmount / 100000000
const fiatValue = btcAmount * price
return fiatValue.toFixed(2)
}
// Usage
const fiatValue = await convertToFiat(
client,
bitcoinNetwork,
'BTC',
'100000000', // 1 BTC in satoshis
FiatCurrency.USD
)
console.log(`Value: $${fiatValue}`)
Price ticker with refresh
Rust
use hydra_client::pricing::{PricingServiceClient, GetAssetFiatPriceRequest, FiatCurrency};
use hydra_client::common::Network;
use tokio::time::{interval, Duration};
use std::sync::Arc;
use tokio::sync::Mutex;
struct PriceTicker {
client: Arc<Mutex<PricingServiceClient<tonic::transport::Channel>>>,
network: Network,
asset_id: String,
fiat_currency: FiatCurrency,
}
impl PriceTicker {
fn new(
client: Arc<Mutex<PricingServiceClient<tonic::transport::Channel>>>,
network: Network,
asset_id: String,
fiat_currency: FiatCurrency,
) -> Self {
Self {
client,
network,
asset_id,
fiat_currency,
}
}
async fn get_price(&self) -> Result<String, Box<dyn std::error::Error>> {
let request = GetAssetFiatPriceRequest {
network: Some(self.network.clone()),
asset_id: self.asset_id.clone(),
fiat_currency: self.fiat_currency as i32,
};
let mut client = self.client.lock().await;
let response = client.get_asset_fiat_price(request).await?;
Ok(response.into_inner().price)
}
async fn start<F>(&self, interval_ms: u64, callback: F)
where
F: Fn(String) + Send + 'static,
{
let mut ticker = interval(Duration::from_millis(interval_ms));
loop {
ticker.tick().await;
match self.get_price().await {
Ok(price) => callback(price),
Err(e) => eprintln!("Failed to fetch price: {}", e),
}
}
}
}
// Usage
let ticker = PriceTicker::new(
Arc::new(Mutex::new(client)),
bitcoin_network,
"BTC".to_string(),
FiatCurrency::Usd,
);
tokio::spawn(async move {
ticker.start(30000, |price| {
println!("BTC/USD: ${}", price);
}).await;
});
// Stop after 5 minutes
tokio::time::sleep(Duration::from_secs(300)).await;
Go
package main
import (
"context"
"fmt"
"log"
"time"
pb "github.com/hydra/api/proto"
)
type PriceTicker struct {
client pb.PricingServiceClient
network *pb.Network
assetId string
fiatCurrency pb.FiatCurrency
stopChan chan bool
}
func NewPriceTicker(
client pb.PricingServiceClient,
network *pb.Network,
assetId string,
fiatCurrency pb.FiatCurrency,
) *PriceTicker {
return &PriceTicker{
client: client,
network: network,
assetId: assetId,
fiatCurrency: fiatCurrency,
stopChan: make(chan bool),
}
}
func (pt *PriceTicker) GetPrice() (string, error) {
request := &pb.GetAssetFiatPriceRequest{
Network: pt.network,
AssetId: pt.assetId,
FiatCurrency: pt.fiatCurrency,
}
response, err := pt.client.GetAssetFiatPrice(context.Background(), request)
if err != nil {
return "", err
}
return response.Price, nil
}
func (pt *PriceTicker) Start(intervalMs int, callback func(string)) {
ticker := time.NewTicker(time.Duration(intervalMs) * time.Millisecond)
go func() {
for {
select {
case <-ticker.C:
price, err := pt.GetPrice()
if err != nil {
log.Printf("Failed to fetch price: %v", err)
} else {
callback(price)
}
case <-pt.stopChan:
ticker.Stop()
return
}
}
}()
}
func (pt *PriceTicker) Stop() {
pt.stopChan <- true
}
// Usage
ticker := NewPriceTicker(
client,
bitcoinNetwork,
"BTC",
pb.FiatCurrency_USD,
)
ticker.Start(30000, func(price string) {
fmt.Printf("BTC/USD: $%s\n", price)
})
// Stop after 5 minutes
time.Sleep(5 * time.Minute)
ticker.Stop()
TypeScript
class PriceTicker {
private client: PricingServiceClient
private network: Network
private assetId: string
private fiatCurrency: FiatCurrency
private interval: NodeJS.Timeout | null = null
constructor(
client: PricingServiceClient,
network: Network,
assetId: string,
fiatCurrency: FiatCurrency
) {
this.client = client
this.network = network
this.assetId = assetId
this.fiatCurrency = fiatCurrency
}
async getPrice(): Promise<string> {
const request = new GetAssetFiatPriceRequest()
request.setNetwork(this.network)
request.setAssetId(this.assetId)
request.setFiatCurrency(this.fiatCurrency)
const response = await this.client.getAssetFiatPrice(request, {})
return response.getPrice()
}
start(intervalMs: number, callback: (price: string) => void) {
this.interval = setInterval(async () => {
try {
const price = await this.getPrice()
callback(price)
} catch (error) {
console.error('Failed to fetch price:', error)
}
}, intervalMs)
}
stop() {
if (this.interval) {
clearInterval(this.interval)
this.interval = null
}
}
}
// Usage
const ticker = new PriceTicker(
client,
bitcoinNetwork,
'BTC',
FiatCurrency.USD
)
ticker.start(30000, (price) => {
console.log(`BTC/USD: $${price}`)
})
// Stop after 5 minutes
setTimeout(() => ticker.stop(), 300000)
Calculate portfolio value
Rust
use hydra_client::pricing::{PricingServiceClient, GetAssetFiatPriceRequest, FiatCurrency};
use hydra_client::wallet::{WalletServiceClient, GetBalancesRequest};
use hydra_client::common::Network;
async fn calculate_portfolio_value(
pricing_client: &mut PricingServiceClient<tonic::transport::Channel>,
wallet_client: &mut WalletServiceClient<tonic::transport::Channel>,
network: Network,
fiat_currency: FiatCurrency,
) -> Result<String, Box<dyn std::error::Error>> {
// Get all balances
let balance_req = GetBalancesRequest {
network: Some(network.clone()),
};
let balance_resp = wallet_client.get_balances(balance_req).await?;
let balances = balance_resp.into_inner().balances;
let mut total_value = 0.0;
// Iterate through each asset
for (asset_id, balance) in balances {
// Get asset price
let price_req = GetAssetFiatPriceRequest {
network: Some(network.clone()),
asset_id: asset_id.clone(),
fiat_currency: fiat_currency as i32,
};
match pricing_client.get_asset_fiat_price(price_req).await {
Ok(price_resp) => {
let price: f64 = price_resp.into_inner().price.parse()?;
// Get total balance (onchain + offchain)
let onchain_confirmed: f64 = balance.onchain
.as_ref()
.and_then(|o| o.confirmed.parse().ok())
.unwrap_or(0.0);
let offchain_local: f64 = balance.offchain
.as_ref()
.and_then(|o| o.local.parse().ok())
.unwrap_or(0.0);
let total_amount = (onchain_confirmed + offchain_local) / 100_000_000.0; // Convert to BTC
total_value += total_amount * price;
}
Err(_) => {
eprintln!("No price available for {}", asset_id);
}
}
}
Ok(format!("{:.2}", total_value))
}
// Usage
let portfolio_value = calculate_portfolio_value(
&mut pricing_client,
&mut wallet_client,
bitcoin_network,
FiatCurrency::Usd,
).await?;
println!("Total Portfolio Value: ${}", portfolio_value);
Go
package main
import (
"context"
"fmt"
"log"
"strconv"
pb "github.com/hydra/api/proto"
)
func calculatePortfolioValue(
pricingClient pb.PricingServiceClient,
walletClient pb.WalletServiceClient,
network *pb.Network,
fiatCurrency pb.FiatCurrency,
) (string, error) {
// Get all balances
balanceReq := &pb.GetBalancesRequest{
Network: network,
}
balanceResp, err := walletClient.GetBalances(context.Background(), balanceReq)
if err != nil {
return "", err
}
balances := balanceResp.Balances
totalValue := 0.0
// Iterate through each asset
for assetId, balance := range balances {
// Get asset price
priceReq := &pb.GetAssetFiatPriceRequest{
Network: network,
AssetId: assetId,
FiatCurrency: fiatCurrency,
}
priceResp, err := pricingClient.GetAssetFiatPrice(context.Background(), priceReq)
if err != nil {
log.Printf("No price available for %s", assetId)
continue
}
price, err := strconv.ParseFloat(priceResp.Price, 64)
if err != nil {
continue
}
// Get total balance (onchain + offchain)
onchainConfirmed := 0.0
if balance.Onchain != nil {
onchainConfirmed, _ = strconv.ParseFloat(balance.Onchain.Confirmed, 64)
}
offchainLocal := 0.0
if balance.Offchain != nil {
offchainLocal, _ = strconv.ParseFloat(balance.Offchain.Local, 64)
}
totalAmount := (onchainConfirmed + offchainLocal) / 100000000 // Convert to BTC
totalValue += totalAmount * price
}
return fmt.Sprintf("%.2f", totalValue), nil
}
// Usage
portfolioValue, err := calculatePortfolioValue(
pricingClient,
walletClient,
bitcoinNetwork,
pb.FiatCurrency_USD,
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Total Portfolio Value: $%s\n", portfolioValue)
TypeScript
async function calculatePortfolioValue(
pricingClient: PricingServiceClient,
walletClient: WalletServiceClient,
network: Network,
fiatCurrency: FiatCurrency
): Promise<string> {
// Get all balances
const balanceReq = new GetBalancesRequest()
balanceReq.setNetwork(network)
const balanceResp = await walletClient.getBalances(balanceReq, {})
const balances = balanceResp.getBalancesMap()
let totalValue = 0
// Iterate through each asset
for (const [assetId, balance] of balances.entries()) {
// Get asset price
const priceReq = new GetAssetFiatPriceRequest()
priceReq.setNetwork(network)
priceReq.setAssetId(assetId)
priceReq.setFiatCurrency(fiatCurrency)
try {
const priceResp = await pricingClient.getAssetFiatPrice(priceReq, {})
const price = parseFloat(priceResp.getPrice())
// Get total balance (onchain + offchain)
const onchainConfirmed = parseFloat(balance.getOnchain()?.getConfirmed() || '0')
const offchainLocal = parseFloat(balance.getOffchain()?.getLocal() || '0')
const totalAmount = (onchainConfirmed + offchainLocal) / 100000000 // Convert to BTC
totalValue += totalAmount * price
} catch (error) {
console.warn(`No price available for ${assetId}`)
}
}
return totalValue.toFixed(2)
}
// Usage
const portfolioValue = await calculatePortfolioValue(
pricingClient,
walletClient,
bitcoinNetwork,
FiatCurrency.USD
)
console.log(`Total Portfolio Value: $${portfolioValue}`)
Supported Fiat Currencies
| Currency | Code | Symbol |
|---|---|---|
| US Dollar | USD | $ |
| Euro | EUR | € |
| British Pound | GBP | £ |
| Japanese Yen | JPY | ¥ |
| Chinese Yuan | CNY | ¥ |
| Australian Dollar | AUD | A$ |
| Canadian Dollar | CAD | C$ |
Price Data Sources
Price data is aggregated from multiple sources including:
- Centralized exchanges (Binance, Coinbase, Kraken)
- Decentralized exchanges (Uniswap, Curve)
- Price oracles (Chainlink)
Prices are updated in real-time and represent current market rates.
Best Practices
- Cache prices - Don't query too frequently, cache for 10-30 seconds
- Handle missing prices - Not all assets have fiat pricing
- Use appropriate decimals - Account for asset decimals when converting
- Batch requests - If checking multiple assets, add delay between calls
- Display update time - Show users when price was last updated
Error Handling
| Error Code | Description | Solution |
|---|---|---|
INVALID_ARGUMENT | Invalid asset_id or network | Verify asset exists on network |
NOT_FOUND | No price available for asset | Asset may not be liquid enough |
UNAVAILABLE | Price service temporarily down | Retry with exponential backoff |
Decimal Precision
Prices are returned as decimal strings with appropriate precision:
- BTC/USD: 2 decimal places (e.g., "98450.75")
- ETH/USD: 2 decimal places (e.g., "3250.50")
- Small assets: May have more decimals for accuracy
Always parse as float or decimal to maintain precision.