Pricing API
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:
| 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: 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. Leavingfiat_currencyunset sends it, so set the currency explicitly on every call.
Response:
| Field | Type | Description |
|---|---|---|
price | DecimalString (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:
| Name | Type | Required | Description |
|---|---|---|---|
asset_symbol | string | YES | Asset ticker symbol (e.g. BTC, ETH, USDC) |
fiat_currency | FiatCurrency | YES | Target fiat currency for the price |
Response:
| Field | Type | Description |
|---|---|---|
price | DecimalString (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. UseGetAssetFiatPriceBySymbolfor 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
| Constant | Value | Currency |
|---|---|---|
FIAT_CURRENCY_UNSPECIFIED | 0 | Invalid default — never send |
FIAT_CURRENCY_USD | 1 | US Dollar |
FIAT_CURRENCY_EUR | 2 | Euro |
FIAT_CURRENCY_GBP | 3 | British Pound |
FIAT_CURRENCY_AUD | 4 | Australian Dollar |
FIAT_CURRENCY_CAD | 5 | Canadian Dollar |
FIAT_CURRENCY_CHF | 6 | Swiss Franc |
FIAT_CURRENCY_CNY | 7 | Chinese Yuan |
FIAT_CURRENCY_JPY | 8 | Japanese Yen |
FIAT_CURRENCY_KRW | 9 | South Korean Won |
FIAT_CURRENCY_RUB | 10 | Russian Ruble |
FIAT_CURRENCY_TRY | 11 | Turkish Lira |
FIAT_CURRENCY_INR | 12 | Indian Rupee |
Which of these actually resolve depends on
pricing_config.fiat_currenciesinconfig.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
- Cache prices — the oracle refreshes in the background, so polling faster than it refreshes buys nothing. 10–30 seconds is plenty.
- Treat an absent
priceas normal — it is an empty optional, not an error. See Staleness. - 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.
- Prefer
GetAssetFiatPricewhen you know the network — it prices that deployment rather than whatever else shares the ticker. - Set
fiat_currencyexplicitly — the zero value isFIAT_CURRENCY_UNSPECIFIED, not USD.
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 | The 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 |
UNAVAILABLE | Price service temporarily down | Retry 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.