Skip to content

feat(xc_admin_frontend): add Lazer integration #2722

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

cctdaniel
Copy link
Contributor

@cctdaniel cctdaniel commented May 23, 2025

Summary

Updated the PythLazer component to display configuration changes in a detailed, structured format similar to PythCore.

Rationale

The original PythLazer component displayed configuration changes as raw JSON, which was difficult to read and understand. This update aligns the user experience with PythCore by:

  • Improved UX: Shows changes in a human-readable table format with clear field names and old/new value comparisons
  • Consistency: Matches the detailed change display style already implemented in PythCore
  • Better Change Visibility: Clearly distinguishes between additions, deletions, and modifications with appropriate titles and strikethrough formatting
  • Type Safety: Properly handles LazerState structure with dedicated components for different change types

The changes also include updates to the underlying validation and type system to properly support the LazerState structure with feeds, publishers, and shard metadata.

How has this been tested?

  • Current tests cover my changes
  • Added new tests
  • Manually tested the code

Manual Testing Steps:

  1. Verified the component renders correctly with the new structured display
  2. Tested upload functionality with sample Lazer configuration files
  3. Confirmed that changes are properly categorized and displayed:
    • Shard configuration changes (shardId, shardName, minRate)
    • Feed changes (metadata fields like minPublishers, symbol, etc.)
    • Publisher changes (name, publicKeys, isActive status)
  4. Validated proper handling of additions, deletions, and modifications
  5. Ensured error handling works correctly with capitalized error messages
  6. Confirmed the modal displays changes with proper formatting and separators

The implementation follows the same patterns as PythCore and integrates seamlessly with the existing validation system and UI components.

Copy link

vercel bot commented May 23, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
proposals ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 29, 2025 11:15am
7 Skipped Deployments
Name Status Preview Comments Updated (UTC)
api-reference ⬜️ Skipped (Inspect) May 29, 2025 11:15am
component-library ⬜️ Skipped (Inspect) May 29, 2025 11:15am
developer-hub ⬜️ Skipped (Inspect) May 29, 2025 11:15am
entropy-debugger ⬜️ Skipped (Inspect) May 29, 2025 11:15am
entropy-explorer ⬜️ Skipped (Inspect) May 29, 2025 11:15am
insights ⬜️ Skipped (Inspect) May 29, 2025 11:15am
staking ⬜️ Skipped (Inspect) May 29, 2025 11:15am

@cctdaniel cctdaniel changed the title feat: add initial Lazer integration feat: add Lazer integration May 23, 2025
@vercel vercel bot temporarily deployed to Preview – insights May 23, 2025 04:41 Inactive
@vercel vercel bot temporarily deployed to Preview – entropy-debugger May 23, 2025 04:41 Inactive
@vercel vercel bot temporarily deployed to Preview – staking May 23, 2025 04:41 Inactive
@vercel vercel bot temporarily deployed to Preview – developer-hub May 23, 2025 04:41 Inactive
@vercel vercel bot temporarily deployed to Preview – component-library May 23, 2025 04:41 Inactive
@vercel vercel bot temporarily deployed to Preview – entropy-explorer May 23, 2025 04:41 Inactive
@vercel vercel bot temporarily deployed to Preview – api-reference May 23, 2025 04:41 Inactive
@vercel vercel bot temporarily deployed to Preview – developer-hub May 23, 2025 04:51 Inactive
@vercel vercel bot temporarily deployed to Preview – component-library May 23, 2025 04:51 Inactive
@vercel vercel bot temporarily deployed to Preview – api-reference May 23, 2025 04:51 Inactive
@vercel vercel bot temporarily deployed to Preview – entropy-debugger May 23, 2025 04:51 Inactive
@vercel vercel bot temporarily deployed to Preview – entropy-explorer May 23, 2025 04:51 Inactive
@vercel vercel bot temporarily deployed to Preview – insights May 23, 2025 04:51 Inactive
@vercel vercel bot temporarily deployed to Preview – staking May 23, 2025 04:51 Inactive
@vercel vercel bot temporarily deployed to Preview – entropy-debugger May 23, 2025 05:11 Inactive
@vercel vercel bot temporarily deployed to Preview – component-library May 23, 2025 05:11 Inactive
@vercel vercel bot temporarily deployed to Preview – staking May 23, 2025 05:11 Inactive
@vercel vercel bot temporarily deployed to Preview – entropy-explorer May 23, 2025 05:11 Inactive
@vercel vercel bot temporarily deployed to Preview – developer-hub May 23, 2025 05:11 Inactive
@vercel vercel bot temporarily deployed to Preview – api-reference May 23, 2025 05:11 Inactive
@vercel vercel bot temporarily deployed to Preview – insights May 23, 2025 05:11 Inactive
@vercel vercel bot temporarily deployed to Preview – developer-hub May 23, 2025 08:24 Inactive
@vercel vercel bot temporarily deployed to Preview – entropy-debugger May 23, 2025 08:24 Inactive
@vercel vercel bot temporarily deployed to Preview – component-library May 23, 2025 08:24 Inactive
@vercel vercel bot temporarily deployed to Preview – api-reference May 23, 2025 10:17 Inactive
@vercel vercel bot temporarily deployed to Preview – component-library May 23, 2025 10:17 Inactive
@vercel vercel bot temporarily deployed to Preview – insights May 23, 2025 10:17 Inactive
Copy link
Collaborator

@cprussin cprussin left a comment

Choose a reason for hiding this comment

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

Biggest change I think that needs to happen here is to rethink some of the types, especially the type for changes. Also I'm calling into question the value of the program_registry.ts abstraction.

Comment on lines +174 to +182
if (
!("shardId" in uploadedLazerState) ||
!("feeds" in uploadedLazerState) ||
!("publishers" in uploadedLazerState)
) {
return {
isValid: false,
error: "Missing required fields in Lazer configuration",
};
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 not guaranteed by the type?

In general I recommend using something like zod to describe and validate schemas in typescript; if this can't be guaranteed by the type system I'd use Zod to parse instead.

}

// Calculate changes between existing and uploaded config
const changes: Record<string, { prev?: any; new?: any }> = {};
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please do not use any -- you can restructure this type slightly by using well known keys to be easier to type (and probably easier to use) with something along these lines:

type ChangeField<T> = { prev: T, new: T };

type Changes = {
  shard: ChangeField<Shard>;
  feeds: Record<string, ChangeField<Feed>>;
  publishers: Record<string, ChangeField<Publisher>>;
}

Comment on lines +126 to +182
export type LazerFeedMetadata = {
priceFeedId: number;
name: string;
symbol: string;
description: string;
assetType: string;
exponent: number;
minPublishers: number;
minRate: string;
expiryTime: string;
isActivated: boolean;
hermesId?: string;
cmcId?: number;
fundingRateInterval?: string;
quoteCurrency?: string;
marketSchedule: string;
};

/**
* Lazer feed type
*/
export type LazerFeed = {
metadata: LazerFeedMetadata;
pendingActivation?: string;
};

/**
* Lazer publisher type
*/
export type LazerPublisher = {
publisherId: number;
name: string;
publicKeys: string[];
isActive: boolean;
};

/**
* Full Lazer state type
*/
export type LazerState = {
shardId: number;
lastSequenceNo: string;
lastTimestamp: string;
shardName: string;
minRate: string;
feeds: LazerFeed[];
publishers: LazerPublisher[];
};

/**
* Lazer-specific configuration type
*/
export type LazerConfig = {
programType: ProgramType.PYTH_LAZER;
// The Lazer state data
state: LazerState;
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

These types are all specific to lazer so they should live alongside the lazer code

Comment on lines +90 to +92
export type DownloadableConfig =
| Record<string, DownloadableProduct>
| LazerState;
Copy link
Collaborator

@cprussin cprussin May 23, 2025

Choose a reason for hiding this comment

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

To me this now looks like two separate types and it's not really clear why the union is ever useful, it doesn't seem like it would ever be usable as a union without the user having to know whether they're working with lazer or core, and if the consumer has to know that, what's the value in having a union or a function that returns the union? If the user knew whether they were working with lazer or core, there's no point in using a function abstracted over both, they should just use the function that's correct for the system they're on.

Even when I look below in this PR where this and the getDownloadableConfig function are being used, they're in PythCore.tsx -- a file explicitly for pyth core -- so then there's no real point in using the abstracted function in program_registry.ts and we should just use the core function from core_functions.ts instead.

Frankly, the more I see stuff like this, the more I question the value of program_registry.ts entirely -- the common interface is useful if the interfaces are common, but it really is starting to look like they aren't common, and the abstraction is not even usable without knowing which program you're working with. If that's the case, it's just added indirection and causes type information to become less precise.

If you can come up with a generic type which Core & Lazer are both concrete implementations of, and define the abstraction in terms of that generic type, and you plan to use them in situations where you know you have a pyth program but you don't know which, I could see it being useful. But if you don't have common arguments AND you don't have common return types, then you should just export the functions from core and lazer separately, and expect the consumers to import the correct one directly.

export type LazerConfig = {
programType: ProgramType.PYTH_LAZER;
// The Lazer state data
state: LazerState;
Copy link
Collaborator

Choose a reason for hiding this comment

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

This type looks redundant, if the type is explicitly a lazer type, why would you also need a programType: ProgramType.PYTH_LAZER ? This is again making me question the program_registry.ts pattern.

const downloadableConfig = getDownloadableConfig(rawConfig) as Record<
string,
DownloadableProduct
>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we need a typecast here? If the return types of getDownloadableConfig are accurate, we shouldn't need the typecast. If they aren't accurate, then we shouldn't be typecasting, we should be fixing the types to be accurate.

([key, newValue]) =>
(isNewShard ||
(changes.prev &&
changes.prev[key as keyof typeof changes.prev] !== newValue)) && (
Copy link
Collaborator

@cprussin cprussin May 23, 2025

Choose a reason for hiding this comment

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

This is kind of awkward, if this is how you plan to use this then I'd rethink the type a bit. The type system can't actually guarantee that the keys are shared between prev and new, which is why you have the typecast. So instead I think it would be better to have the type be something where you have prev and new underneath each key, i.e. something along the lines of (referencing my prior comment):

type Changes = {
  shard: Record<keyof Shard, ChangeField<Shard[keyof Shard]>>;
  feeds: Record<string, ChangeField<Feed>>;
  publishers: Record<string, ChangeField<Publisher>>;
}

In general, typecasting should be avoided, and if you have to use it for stuff like this, it generally indicates that you should rethink the types you're working with.

}

fetchLazerState()
}, [])
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm a bit confused why this is async, it looks like everything it does is sync?

Also, you might like this pattern which I always use for client-side data fetching: https://github.com/pyth-network/pyth-crosschain/blob/main/packages/component-library/src/useData/index.ts. It has a few nice things:

  • This uses swr so you can easily configure it to automatically revalidate data if you so desire
  • It returns a tagged union of states so handling loading / error / before loading states is not only trivial, but typescript will actually enforce that you don't forget to do it

Here's an example of recent code I wrote that uses it: https://github.com/pyth-network/pyth-crosschain/blob/main/apps/entropy-explorer/src/components/Home/results.tsx#L31-L62

@vercel vercel bot temporarily deployed to Preview – component-library May 27, 2025 05:46 Inactive
@vercel vercel bot temporarily deployed to Preview – entropy-debugger May 27, 2025 05:46 Inactive
@vercel vercel bot temporarily deployed to Preview – staking May 27, 2025 05:46 Inactive
@vercel vercel bot temporarily deployed to Preview – entropy-explorer May 27, 2025 05:46 Inactive
@vercel vercel bot temporarily deployed to Preview – developer-hub May 27, 2025 05:46 Inactive
@vercel vercel bot temporarily deployed to Preview – api-reference May 27, 2025 05:46 Inactive
@vercel vercel bot temporarily deployed to Preview – insights May 27, 2025 05:46 Inactive
@ali-bahjati
Copy link
Collaborator

@cctdaniel I'm thinking that maybe you can actually use protobuf codegen to generate the state types as well.

@vercel vercel bot temporarily deployed to Preview – component-library May 29, 2025 11:10 Inactive
@vercel vercel bot temporarily deployed to Preview – staking May 29, 2025 11:10 Inactive
@vercel vercel bot temporarily deployed to Preview – entropy-debugger May 29, 2025 11:10 Inactive
@vercel vercel bot temporarily deployed to Preview – api-reference May 29, 2025 11:10 Inactive
@vercel vercel bot temporarily deployed to Preview – insights May 29, 2025 11:10 Inactive
@vercel vercel bot temporarily deployed to Preview – developer-hub May 29, 2025 11:10 Inactive
@vercel vercel bot temporarily deployed to Preview – entropy-explorer May 29, 2025 11:10 Inactive
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.

3 participants