Api

Pricing API

Asset price feeds

The Pricing API provides fiat price conversion for assets.

JSON-RPC namespace: pricing

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: the constants are FIAT_CURRENCY_*, not bare currency codes — see Supported fiat currencies for the full table with numeric values.

FIAT_CURRENCY_UNSPECIFIED (0) is the invalid default. Leaving fiat_currency unset sends it, so set the currency explicitly on every call.

Response:

FieldTypeDescription
priceDecimalString (optional)Price in the requested fiat currency, absent when no price is available — see Staleness

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:5003").await?;

    let request = GetAssetFiatPriceRequest {
        network: Some(Network {
            protocol: Protocol::Bitcoin as i32,
            id: "0a03cf40".to_string(),
        }),
        asset_id: "0x0000000000000000000000000000000000000000000000000000000000000000".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:5003", 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: pb.Protocol_PROTOCOL_BITCOIN,
            Id:       "0a03cf40",
        },
        AssetId:      "0x0000000000000000000000000000000000000000000000000000000000000000",
        FiatCurrency: pb.FiatCurrency_FIAT_CURRENCY_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:5003')

const request = new GetAssetFiatPriceRequest()
request.setNetwork({ protocol: 1, id: '0a03cf40' })
request.setAssetId('0x0000000000000000000000000000000000000000000000000000000000000000')
request.setFiatCurrency(FiatCurrency.FIAT_CURRENCY_USD)

const response = await client.getAssetFiatPrice(request, {})
const price = response.getPrice()
console.log(`BTC Price: $${price}`)

Example Response:

{
  "price": "98450.75"
}

Get Asset Fiat Price By Symbol

Returns the fiat price of an asset by its ticker symbol. Does not require specifying a network — useful for general price lookups (e.g. BTC, ETH, USDC).

Method: GetAssetFiatPriceBySymbol

Parameters:

NameTypeRequiredDescription
asset_symbolstringYESAsset ticker symbol (e.g. BTC, ETH, USDC)
fiat_currencyFiatCurrencyYESTarget fiat currency for the price

Response:

FieldTypeDescription
priceDecimalString (optional)Fiat price, or empty if not available

Example Request:

import { GetAssetFiatPriceBySymbolRequest } from './proto/pricing_pb'

const request = new GetAssetFiatPriceBySymbolRequest()
request.setAssetSymbol('BTC')
request.setFiatCurrency(FiatCurrency.FIAT_CURRENCY_USD) // 1

const response = await client.getAssetFiatPriceBySymbol(request, {})
const price = response.getPrice()
if (price) {
  console.log('BTC price (USD):', price.getValue())
} else {
  console.log('Price not available')
}

Example Response:

{ "price": "67234.50" }

Tip: prefer GetAssetFiatPrice (network + asset_id) when you already know which network the asset lives on — it's unambiguous. Use GetAssetFiatPriceBySymbol for generic price displays where the user just wants to know "what's BTC worth?".


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>> {
    fn response_price(
        resp: tonic::Response<hydra_client::pricing::GetAssetFiatPriceResponse>,
    ) -> Option<String> {
        resp.into_inner().price.map(|p| p.value)
    }

    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,
        };

        // `price` is optional: absent means no price is available.
        if let Some(price) = response_price(client.get_asset_fiat_price(request).await?) {
            prices.insert(asset_id, price);
        }
    }

    Ok(prices)
}

// Usage — every id must live on the network you pass.
let prices = get_asset_prices(
    &mut client,
    arbitrum_sepolia_network,
    vec![
        "0x0000000000000000000000000000000000000000".to_string(),
        "erc20:0x1baabb04529d43a73232b713c0fe471f7c7334d5".to_string(),
    ],
    FiatCurrency::Usd,
).await?;

println!("ETH:  {:?}", prices.get("0x0000000000000000000000000000000000000000"));
println!("USDC: {:?}", prices.get("erc20:0x1baabb04529d43a73232b713c0fe471f7c7334d5"));
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
        }

        // Price is optional: nil means no price is available.
        if response.Price != nil {
            prices[assetId] = response.Price.Value
        }
    }

    return prices, nil
}

// Usage — every id must live on the network you pass.
prices, err := getAssetPrices(
    client,
    arbitrumSepoliaNetwork,
    []string{"0x0000000000000000000000000000000000000000", "erc20:0x1baabb04529d43a73232b713c0fe471f7c7334d5"},
    pb.FiatCurrency_FIAT_CURRENCY_USD,
)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("ETH:  %s\n", prices["0x0000000000000000000000000000000000000000"])
fmt.Printf("USDC: %s\n", prices["erc20:0x1baabb04529d43a73232b713c0fe471f7c7334d5"])
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)

    // getPrice() is optional: undefined means no price is available.
    const price = (await client.getAssetFiatPrice(request, {})).getPrice()
    if (price) prices.set(assetId, price.getValue())
  }

  return prices
}

// Usage — every id must live on the network you pass.
const prices = await getAssetPrices(
  client,
  arbitrumSepoliaNetwork,
  ['0x0000000000000000000000000000000000000000', 'erc20:0x1baabb04529d43a73232b713c0fe471f7c7334d5'],
  FiatCurrency.FIAT_CURRENCY_USD
)

console.log('ETH: ', prices.get('0x0000000000000000000000000000000000000000'))
console.log('USDC:', prices.get('erc20:0x1baabb04529d43a73232b713c0fe471f7c7334d5'))

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,
    "0x0000000000000000000000000000000000000000000000000000000000000000".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,
    "0x0000000000000000000000000000000000000000000000000000000000000000",
    "100000000", // 1 BTC in satoshis
    pb.FiatCurrency_FIAT_CURRENCY_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,
  '0x0000000000000000000000000000000000000000000000000000000000000000',
  '100000000', // 1 BTC in satoshis
  FiatCurrency.FIAT_CURRENCY_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,
    "0x0000000000000000000000000000000000000000000000000000000000000000".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,
    "0x0000000000000000000000000000000000000000000000000000000000000000",
    pb.FiatCurrency_FIAT_CURRENCY_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,
  '0x0000000000000000000000000000000000000000000000000000000000000000',
  FiatCurrency.FIAT_CURRENCY_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_FIAT_CURRENCY_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.FIAT_CURRENCY_USD
)

console.log(`Total Portfolio Value: $${portfolioValue}`)

Supported Fiat Currencies

ConstantValueCurrency
FIAT_CURRENCY_UNSPECIFIED0Invalid default — never send
FIAT_CURRENCY_USD1US Dollar
FIAT_CURRENCY_EUR2Euro
FIAT_CURRENCY_GBP3British Pound
FIAT_CURRENCY_AUD4Australian Dollar
FIAT_CURRENCY_CAD5Canadian Dollar
FIAT_CURRENCY_CHF6Swiss Franc
FIAT_CURRENCY_CNY7Chinese Yuan
FIAT_CURRENCY_JPY8Japanese Yen
FIAT_CURRENCY_KRW9South Korean Won
FIAT_CURRENCY_RUB10Russian Ruble
FIAT_CURRENCY_TRY11Turkish Lira
FIAT_CURRENCY_INR12Indian Rupee

Which of these actually resolve depends on pricing_config.fiat_currencies in config.yaml — the oracle is only asked to quote the currencies the node lists. A currency the node does not carry comes back with no price, the same as any other unavailable price.


Price Data Sources

Prices come from the price oracle configured in config.yaml (pricing_config.price_oracle_url), which refreshes them in the background. Hydra App does not poll an exchange itself — it asks the oracle.

How an asset is looked up

(2026-09-03) The oracle is asked for that specific deployment of the asset — this contract, on this network. Only when the oracle does not list the asset by deployment is it asked for by the symbol the node holds it under (which is the declared symbol when the operator set one, otherwise the chain's).

Looking up by deployment first is what keeps a bridged token priced as itself rather than as whatever else shares its ticker.

Staleness

(2026-09-03) A price the oracle flags as stale — not refreshed within its maximum age — is reported as unavailable rather than served.

Handle "no price" as a normal outcome, not an error. A quiet asset, an oracle hiccup, or a newly listed token will all produce it. Anything that must not block on pricing (order sizing, balance display) needs a path that works without a fiat figure. Serving a stale price is the failure mode this deliberately avoids.


Best Practices

  1. Cache prices — the oracle refreshes in the background, so polling faster than it refreshes buys nothing. 10–30 seconds is plenty.
  2. Treat an absent price as normal — it is an empty optional, not an error. See Staleness.
  3. Never make a fund-moving decision from a fiat price. It is a display and sizing aid; the amounts the API moves are always in the asset's own units.
  4. Prefer GetAssetFiatPrice when you know the network — it prices that deployment rather than whatever else shares the ticker.
  5. Set fiat_currency explicitly — the zero value is FIAT_CURRENCY_UNSPECIFIED, not USD.

Error Handling

Error CodeDescriptionSolution
INVALID_ARGUMENTInvalid asset_id or networkVerify asset exists on network
NOT_FOUNDNo price available for assetThe oracle does not list this deployment or its symbol, or the price it holds is stale. Expected for illiquid and newly listed assets — degrade gracefully rather than retrying hard
UNAVAILABLEPrice service temporarily downRetry with exponential backoff

Decimal Precision

price is a DecimalString — an arbitrary-precision decimal in the fiat currency, carrying whatever precision the oracle published. There is no fixed number of decimal places: a large-cap asset typically comes back with two, a micro-cap with many more.

Parse it with a decimal type (rust_decimal, big.Float/shopspring/decimal, Big.js), never a binary float, and round only at the point you render it.


← Back to API Reference | Next: Client API →


Copyright © 2025