Skip to content

Conversation

tejasbadadare
Copy link
Contributor

@tejasbadadare tejasbadadare commented Oct 7, 2025

Summary

Add types for output representations of the Metadata v3 API.

  • FeedResponseV3: Returned by the /v3/feeds APIs
  • AssetResponseV3: Returned by the /v3/assets APIs
  • Existing /v1/symbols API will continue to return the history_service::api::SymbolResponse from Lazer internals for the time being. In the future, this type should be unified with the duplicated one in the protocol crate (pyth_lazer_protocol::jrpc::SymbolMetadata, used by History client).

How has this been tested?

  • Current tests cover my changes
  • Added new tests
    • Added basic ser/de roundtrip tests, added validation tests for SymbolV3 format
  • Manually tested the code

Copy link

vercel bot commented Oct 7, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
api-reference Ready Ready Preview Comment Oct 9, 2025 11:34pm
component-library Ready Ready Preview Comment Oct 9, 2025 11:34pm
developer-hub Ready Ready Preview Comment Oct 9, 2025 11:34pm
entropy-explorer Ready Ready Preview Comment Oct 9, 2025 11:34pm
insights Ready Ready Preview Comment Oct 9, 2025 11:34pm
proposals Ready Ready Preview Comment Oct 9, 2025 11:34pm
staking Ready Ready Preview Comment Oct 9, 2025 11:34pm

keyvankhademi
keyvankhademi previously approved these changes Oct 7, 2025
/// Commodity
Commodity,
/// Funding rate
FundingRate,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this really an asset class?

Copy link
Contributor Author

@tejasbadadare tejasbadadare Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm.. we currently consider it an asset class in the V1 API, but i agree that conceptually it doesn't really fit. We can introduce an AssetClassV3 that excludes these.

But then the question is what we'd set for asset_class for funding rate feeds. Maybe it's reasonable to leave it as FundingRate? Or we can set it to the underlying asset's class. Will check with @YaserJazouane on this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caught up, yes funding rate is not an asset class. It'll be a feed_kind (as it already is).

/// High-level asset class.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum AssetClass {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of the reasons for opting in a dynamic metadata was to not go through a code change when new asset classes are added to the system. I know that users (on both sides) rely on these, and that's probably why you opted for explicit definition here. But if that's the case, you might actually remove any dynamic field and make everything very explicit. Being in the middle (some explicit metadata, some implicit dynamic) is probably worse.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should stick with our decision and use fully dynamic metadata in the protocols. In Rust that would be BTreeMap<String, serde_value::Value>. We can revisit it later if we feel like the metadata structure is very stable and future-proof, but I doubt that it will happen soon.

Copy link
Contributor Author

@tejasbadadare tejasbadadare Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah my main goal in adding these explicit types is to ensure end users can depend on a stable API contract across different versions. I.e. the different types like the existing SymbolResponse and the new FeedResponseV3 can handle the differences between v1 and v3 representations derived from the same internal dynamic metadata representation.

/// Unique human-readable identifier for a feed.
/// Format: `source.instrument_type.base/quote`
/// Examples: `"pyth.spot.btc/usd"`, `"pyth.redemptionrate.alp/usd"`, `"binance.fundingrate.btc/usdt"`, `"pyth.future.emz5/usd"`
pub symbol: String,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i recommend opting for a specific type with validation here to ensure it's correct.

q: what if the asset has no quote asset? (such as rates)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A type with validation to enforce the string format is a good idea.
Not sure about a feed with no quote asset. If such a feed is valid it poses a deeper problem with the data model, since it revolves around feed being an asset/quote pair. Fwiw currently funding rates are USD denominated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay caught up with @YaserJazouane on this and yep we should make the quote asset optional. There are cases like yields, indices, fundingrates that aren't quoted in any currency. Our existing funding rate example is denominated in USD which is incorrect.

tldr:

  • We're amending the format to be source.instrument_type.base[/quote] where the quote asset is optional
  • We're making quote_asset_id optional

/// CoinMarketCap asset identifier.
/// Example: `"123"`
#[serde(skip_serializing_if = "Option::is_none")]
pub cmc_id: Option<String>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i wonder whether you considered @jayantk suggestion on having a nested struct as "external mappings". It's probably more difficult on the DB layer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah i did consider it, but i figured that a nested struct in the dynamic metadata opened the door to making its own table to ensure proper types in the DB. FIgured it was simpler to leave it flat.

pub feed_expiry: Option<String>,
/// The nature of the data produced by the feed.
/// Examples: `"price"`, `"fundingRate"`
pub feed_kind: FeedKind,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm so we do have a feedkind and an instrument type. I wonder whether we can merge them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think they're capturing slightly different things.
Afaik feed_kind is meant to be the coarsest discriminator of feeds, which we use to trigger different aggregation logic (price feeds, non price feeds like funding rates). Whereas instrument_type is a bit more specific, and describes the pricing context (spot price, averaged price, etc.).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consensus on this topic was that the two fields capture different things as mentioned above, so planning on keeping it this way.

@keyvankhademi keyvankhademi dismissed their stale review October 7, 2025 17:24

pending lazer PR

…String to InstrumentType for string parsing, add tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants