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:

NameTypeRequiredDescription
networkNetworkYESNetwork where asset exists
asset_idstringYESAsset identifier
fiat_currencyFiatCurrencyYESTarget fiat currency

FiatCurrency Enum:

ValueDescription
USDUS Dollar
EUREuro
GBPBritish Pound
JPYJapanese Yen
CNYChinese Yuan
AUDAustralian Dollar
CADCanadian Dollar

Response:

FieldTypeDescription
priceDecimalStringPrice 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

CurrencyCodeSymbol
US DollarUSD$
EuroEUR
British PoundGBP£
Japanese YenJPY¥
Chinese YuanCNY¥
Australian DollarAUDA$
Canadian DollarCADC$

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

  1. Cache prices - Don't query too frequently, cache for 10-30 seconds
  2. Handle missing prices - Not all assets have fiat pricing
  3. Use appropriate decimals - Account for asset decimals when converting
  4. Batch requests - If checking multiple assets, add delay between calls
  5. Display update time - Show users when price was last updated

Error Handling

Error CodeDescriptionSolution
INVALID_ARGUMENTInvalid asset_id or networkVerify asset exists on network
NOT_FOUNDNo price available for assetAsset may not be liquid enough
UNAVAILABLEPrice service temporarily downRetry 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.


← Back to API Reference | Next: Client API →


Copyright © 2025