Api

Asset API

Asset registry and token management

The Asset API exposes Hydra App's AssetService — the registry of assets the node tracks on each network. Every network has one native asset; tokens are whatever the operator has registered, from config.yaml at boot or with AddToken at runtime.

JSON-RPC namespace: asset

Endpoints


Get Assets

Get all registered assets for a specific network.

Service: AssetServiceMethod: GetAssets

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork to query assets for

Response:

FieldTypeDescription
assetsAsset[]Array of registered assets

Asset Object:

FieldTypeDescription
idstringAsset identifier — see Asset ID formats
namestringHuman-readable name, e.g. "Bitcoin"
symbolstringTicker, e.g. "BTC". The declared symbol when the operator set one, otherwise the chain's
decimalsuint32Decimal places in the asset's smallest unit. Always read from the chain

There is no is_native flag. An earlier revision of this page listed one; the proto has never carried it. An asset is native exactly when its id is its protocol's zero address — or ask for it directly with GetNativeAsset.

Example Request:

TypeScript
const assets = await hydraGrpcClient.getAssets({
  network: { protocol: 2, id: '11155111' }   // Ethereum Sepolia
})
Rust
let request = tonic::Request::new(GetAssetsRequest {
    network: Some(Network {
        protocol: Protocol::Evm as i32,
        id: "11155111".to_string(),
    }),
});

let response = client.get_assets(request).await?;
let assets = response.into_inner().assets;

Get Asset

Get information about a specific asset.

Service: AssetServiceMethod: GetAsset

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork the asset is on
asset_idstringYESAsset identifier

Response:

FieldTypeDescription
assetAssetAsset details

Example Request:

TypeScript
const asset = await hydraGrpcClient.getAsset({
  network: { protocol: 2, id: '11155111' },
  assetId: 'erc20:0x8cd0da3d001b013336918b8bc4e56d9dda1347e0'   // USDC on Sepolia
})
Rust
let request = tonic::Request::new(GetAssetRequest {
    network: Some(Network {
        protocol: Protocol::Evm as i32,
        id: "11155111".to_string(),
    }),
    asset_id: "erc20:0x8cd0da3d001b013336918b8bc4e56d9dda1347e0".to_string(),
});

let response = client.get_asset(request).await?;
let asset = response.into_inner().asset;

Get Native Asset

Get the native asset for a network (e.g., BTC for Bitcoin, ETH for Ethereum).

Service: AssetServiceMethod: GetNativeAsset

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork to get native asset for

Response:

FieldTypeDescription
assetAssetNative asset details

Example Request:

TypeScript
const nativeAsset = await hydraGrpcClient.getNativeAsset({
  network: { protocol: 1, id: '0a03cf40' }   // Bitcoin Signet
})
// → { id: "0x0000…0000", name: "Bitcoin", symbol: "BTC", decimals: 8 }
Rust
let request = tonic::Request::new(GetNativeAssetRequest {
    network: Some(Network {
        protocol: Protocol::Bitcoin as i32,
        id: "0a03cf40".to_string(),
    }),
});

let response = client.get_native_asset(request).await?;
let asset = response.into_inner().asset;

Add Token

Register a new token to the asset registry.

Service: AssetServiceMethod: AddToken

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork the token is on
token_idstringYESThe token identifier — an ERC-20 / TRC-20 contract address
symbolstringno(2026-09-03) Ticker to show in place of the one the contract reports
namestringno(2026-09-03) Name to show in place of the one the contract reports

The request field is token_id, not token_address. Older revisions of this page said token_address; that name has never been on the wire.

Response:

FieldTypeDescription
assetAssetAdded asset details, fetched from the blockchain

Declaring a symbol and name

A contract's symbol() is its deployer's choice — a bridged or test deployment often carries a variant of the asset's real ticker — while every consumer of your node expects the asset's own identity. Setting symbol / name replaces the chain's wherever the node hands the token out.

The overrides are re-applied on every registration of that token, so changing them is a matter of calling AddToken again. Omit either to keep the chain's own value.

Decimals are never declared. They define the token's units and are only ever read from the chain. There is no field for them, by design — a wrong decimals value would silently misprice every amount.

Constraints, enforced at boot when declared in config.yaml and at the call otherwise: a symbol must be a single word with no whitespace; a name must be non-empty with no leading or trailing whitespace.

The same overrides can be set declaratively per network — see the Setup Guide:

tokens:
  - "erc20:0xaf88d065e77c8cC2239327C5EDb3A432268e5831"   # USDC — keeps the chain's symbol
  - id: "erc20:0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"
    symbol: USDT                                          # contract reports "USD₮0"
    name: "Tether USD"

Example Request:

TypeScript
// Add USDC token on Ethereum Sepolia
const asset = await hydraGrpcClient.addToken({
  network: { protocol: 2, id: '11155111' },
  tokenId: 'erc20:0x8cd0da3d001b013336918b8bc4e56d9dda1347e0'
})

// With display overrides — LayerZero-bridged USDT on Arbitrum reports "USD₮0"
const usdt = await hydraGrpcClient.addToken({
  network: { protocol: 2, id: '42161' },
  tokenId: 'erc20:0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9',
  symbol: 'USDT',
  name: 'Tether USD'
})
Rust
let request = tonic::Request::new(AddTokenRequest {
    network: Some(Network {
        protocol: Protocol::Evm as i32,
        id: "11155111".to_string(),
    }),
    token_id: "erc20:0x8cd0da3d001b013336918b8bc4e56d9dda1347e0".to_string(),
    symbol: None,
    name: None,
});

let response = client.add_token(request).await?;
let asset = response.into_inner().asset;

Common Patterns

Get all assets including native

Rust
async fn get_all_network_assets(
    client: &mut AssetServiceClient<Channel>,
    network: Network,
) -> Result<Vec<Asset>, Box<dyn std::error::Error>> {
    let response = client
        .get_assets(GetAssetsRequest {
            network: Some(network),
        })
        .await?;

    Ok(response.into_inner().assets)
}

Error Handling

Error CodeDescriptionSolution
INVALID_ARGUMENTInvalid network or asset IDVerify parameters
NOT_FOUNDAsset not foundCheck asset exists on this network
ALREADY_EXISTSToken already registeredUse GetAsset instead

Best Practices

  1. Cache the registry. It changes only when the operator adds a token or restarts the node — fetch it once at start and refresh on demand.
  2. Verify a contract before adding it. AddToken reads whatever metadata the contract reports; a lookalike contract is registered just as happily as the real one.
  3. Use decimals for display only. Amounts on the wire are already whole-unit DecimalStrings — see Amount precision. decimals is what you need to convert to and from chain-native values, not to read the API.
  4. Fetch the native asset for fee displayGetNativeAsset. Every on-chain fee is quoted in it, whatever asset the transaction moves.
  5. Register a token before using it. Balances, sends, channels and orders all reject an asset the node does not hold in its registry.

← Back to API Reference | Next: Blockchain API →


Copyright © 2025