Skip to content

Commit 9c27269

Browse files
committed
Merge branch 'main' into fix/suspense-props
2 parents b3f9c31 + c166d1c commit 9c27269

File tree

30 files changed

+1113
-647
lines changed

30 files changed

+1113
-647
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Fortuna is an off-chain service which can be used by [Entropy](https://pyth.netw
4141

4242
Please install the following tools in order to work in this repository:
4343

44-
- [NVM](https://github.com/nvm-sh/nvm?tab=readme-ov-file#installing-and-updating) to manage your node version, then run `nvm use 20` to ensure you are using node version 20.
44+
- [NVM](https://github.com/nvm-sh/nvm?tab=readme-ov-file#installing-and-updating) to manage your node version, then run `nvm use 22` to ensure you are using node version 22.
4545
- [Foundry](https://book.getfoundry.sh/getting-started/installation) in order to use `forge` for Ethereum contract development
4646
- [Solana CLI](https://solana.com/docs/intro/installation) for working with Solana programs.
4747
- After installing, please run `solana keygen new` to generate a local private key.

apps/insights/next.config.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ const config = {
33
useCache: true,
44
reactCompiler: true,
55
},
6-
76
reactStrictMode: true,
87

98
pageExtensions: ["ts", "tsx", "mdx"],

apps/insights/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@
2828
"@pythnetwork/known-publishers": "workspace:*",
2929
"@react-hookz/web": "catalog:",
3030
"@solana/web3.js": "catalog:",
31+
"async-cache-dedupe": "catalog:",
3132
"bs58": "catalog:",
3233
"clsx": "catalog:",
3334
"cryptocurrency-icons": "catalog:",
3435
"dnum": "catalog:",
36+
"ioredis": "^5.7.0",
3537
"lightweight-charts": "catalog:",
3638
"motion": "catalog:",
3739
"next": "catalog:",
@@ -44,7 +46,8 @@
4446
"superjson": "catalog:",
4547
"swr": "catalog:",
4648
"zod": "catalog:",
47-
"zod-validation-error": "catalog:"
49+
"zod-validation-error": "catalog:",
50+
"zod-search-params": "catalog:"
4851
},
4952
"devDependencies": {
5053
"@cprussin/eslint-config": "catalog:",
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { NextRequest } from "next/server";
2+
import { stringify } from "superjson";
3+
import { z } from "zod";
4+
import { parseSearchParams } from "zod-search-params";
5+
6+
import {
7+
Cluster,
8+
CLUSTER_NAMES,
9+
ClusterToName,
10+
toCluster,
11+
} from "../../../../../services/pyth";
12+
import { getFeeds } from "../../../../../services/pyth/get-feeds";
13+
14+
export const GET = async (
15+
request: NextRequest,
16+
{ params }: { params: Promise<{ publisher: string }> },
17+
) => {
18+
const { publisher } = await params;
19+
const searchParams = request.nextUrl.searchParams;
20+
const parsedSearchParams = parseSearchParams(queryParamsSchema, searchParams);
21+
22+
if (!parsedSearchParams) {
23+
return new Response("Invalid params", {
24+
status: 400,
25+
});
26+
}
27+
28+
const { cluster } = parsedSearchParams;
29+
30+
if (!publisher) {
31+
return new Response("Publisher is required", {
32+
status: 400,
33+
});
34+
}
35+
36+
const feeds = await getFeeds(cluster);
37+
38+
const filteredFeeds = feeds.filter((feed) =>
39+
feed.price.priceComponents.some((c) => c.publisher === publisher),
40+
);
41+
42+
return new Response(stringify(filteredFeeds), {
43+
headers: {
44+
"Content-Type": "application/json",
45+
},
46+
});
47+
};
48+
49+
const queryParamsSchema = z.object({
50+
cluster: z
51+
.enum(CLUSTER_NAMES)
52+
.transform((value) => toCluster(value))
53+
.default(ClusterToName[Cluster.Pythnet]),
54+
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { NextRequest } from "next/server";
2+
import { stringify } from "superjson";
3+
import { z } from "zod";
4+
import { parseSearchParams } from "zod-search-params";
5+
6+
import {
7+
Cluster,
8+
CLUSTER_NAMES,
9+
ClusterToName,
10+
toCluster,
11+
} from "../../../../../services/pyth";
12+
import { getFeeds } from "../../../../../services/pyth/get-feeds";
13+
14+
export const GET = async (
15+
request: NextRequest,
16+
{ params }: { params: Promise<{ symbol: string }> },
17+
) => {
18+
const { symbol } = await params;
19+
const searchParams = request.nextUrl.searchParams;
20+
const parsedSearchParams = parseSearchParams(queryParamsSchema, searchParams);
21+
22+
if (!parsedSearchParams) {
23+
return new Response("Invalid params", {
24+
status: 400,
25+
});
26+
}
27+
28+
const { cluster } = parsedSearchParams;
29+
30+
const feeds = await getFeeds(cluster);
31+
const feed = feeds.find((feed) => feed.symbol === symbol);
32+
33+
return new Response(stringify(feed), {
34+
headers: {
35+
"Content-Type": "application/json",
36+
},
37+
});
38+
};
39+
40+
const queryParamsSchema = z.object({
41+
cluster: z
42+
.enum(CLUSTER_NAMES)
43+
.transform((value) => toCluster(value))
44+
.default(ClusterToName[Cluster.Pythnet]),
45+
});
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { NextRequest } from "next/server";
2+
import { stringify } from "superjson";
3+
import { z } from "zod";
4+
import { parseSearchParams } from "zod-search-params";
5+
6+
import { CLUSTER_NAMES, toCluster } from "../../../../services/pyth";
7+
import { getFeeds } from "../../../../services/pyth/get-feeds";
8+
9+
export const GET = async (request: NextRequest) => {
10+
// get cluster from query params
11+
const searchParams = request.nextUrl.searchParams;
12+
const parsedSearchParams = parseSearchParams(queryParamsSchema, searchParams);
13+
14+
if (!parsedSearchParams) {
15+
return new Response("Invalid params", {
16+
status: 400,
17+
});
18+
}
19+
20+
const { excludePriceComponents, cluster } = parsedSearchParams;
21+
22+
const feeds = await getFeeds(cluster);
23+
const filteredFeeds = excludePriceComponents
24+
? feeds.map((feed) => {
25+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
26+
const { price, ...rest } = feed;
27+
return rest;
28+
})
29+
: feeds;
30+
31+
return new Response(stringify(filteredFeeds), {
32+
headers: {
33+
"Content-Type": "application/json",
34+
},
35+
});
36+
};
37+
38+
const queryParamsSchema = z.object({
39+
cluster: z.enum(CLUSTER_NAMES).transform((value) => toCluster(value)),
40+
excludePriceComponents: z.boolean(),
41+
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { NextRequest, NextResponse } from "next/server";
2+
import { z } from "zod";
3+
import { parseSearchParams } from "zod-search-params";
4+
5+
import {
6+
Cluster,
7+
CLUSTER_NAMES,
8+
ClusterToName,
9+
toCluster,
10+
} from "../../../../../services/pyth";
11+
import { getPublishersForCluster } from "../../../../../services/pyth/get-publishers-for-cluster";
12+
13+
export const GET = async (
14+
request: NextRequest,
15+
{ params }: { params: Promise<{ symbol: string }> },
16+
) => {
17+
const { symbol } = await params;
18+
const searchParams = request.nextUrl.searchParams;
19+
const parsedSearchParams = parseSearchParams(queryParamsSchema, searchParams);
20+
21+
if (!parsedSearchParams) {
22+
return new Response("Invalid params", {
23+
status: 400,
24+
});
25+
}
26+
27+
const { cluster } = parsedSearchParams;
28+
29+
if (!symbol) {
30+
return new Response("Symbol is required", {
31+
status: 400,
32+
});
33+
}
34+
35+
const map = await getPublishersForCluster(cluster);
36+
37+
return NextResponse.json(map[symbol] ?? []);
38+
};
39+
40+
const queryParamsSchema = z.object({
41+
cluster: z
42+
.enum(CLUSTER_NAMES)
43+
.transform((value) => toCluster(value))
44+
.default(ClusterToName[Cluster.Pythnet]),
45+
});

apps/insights/src/app/component-score-history/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ export const GET = async (req: NextRequest) => {
1616
);
1717
if (parsed.success) {
1818
const { cluster, publisherKey, symbol, from, to } = parsed.data;
19-
const data = await getFeedScoreHistory(
19+
const data = await getFeedScoreHistory({
2020
cluster,
2121
publisherKey,
2222
symbol,
2323
from,
2424
to,
25-
);
25+
});
2626
return Response.json(data);
2727
} else {
2828
return new Response(fromError(parsed.error).toString(), {

apps/insights/src/app/historical-prices/route.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ export async function GET(req: NextRequest) {
66
const symbol = req.nextUrl.searchParams.get("symbol");
77
const until = req.nextUrl.searchParams.get("until");
88
if (symbol && until) {
9-
const res = await getHistoricalPrices(decodeURIComponent(symbol), until);
9+
const res = await getHistoricalPrices({
10+
symbol: decodeURIComponent(symbol),
11+
until,
12+
});
1013
return Response.json(res);
1114
} else {
1215
return new Response("Must provide `symbol` and `until`", { status: 400 });

apps/insights/src/app/price-feeds/[slug]/layout.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import type { Metadata } from "next";
22
import { notFound } from "next/navigation";
33
import type { ReactNode } from "react";
44

5-
import { Cluster, getFeeds } from "../../../services/pyth";
5+
import { Cluster } from "../../../services/pyth";
6+
import { getFeeds } from "../../../services/pyth/get-feeds";
67

78
export { PriceFeedLayout as default } from "../../../components/PriceFeed/layout";
89

0 commit comments

Comments
 (0)