Ver código fonte

Merge pull request #1762 from cprussin/api-reference-improvements

Implement API reference feedback
Connor Prussin 1 ano atrás
pai
commit
4d61c7a695
46 arquivos alterados com 633 adições e 825 exclusões
  1. 1 0
      apps/api-reference/.gitignore
  2. 2 6
      apps/api-reference/next.config.js
  3. 3 5
      apps/api-reference/package.json
  4. 1 0
      apps/api-reference/public/.well-known/walletconnect.txt
  5. 4 15
      apps/api-reference/src/apis/evm/common.ts
  6. 2 0
      apps/api-reference/src/apis/evm/get-ema-price-no-older-than.ts
  7. 2 0
      apps/api-reference/src/apis/evm/get-ema-price-unsafe.ts
  8. 2 0
      apps/api-reference/src/apis/evm/get-ema-price.ts
  9. 2 0
      apps/api-reference/src/apis/evm/get-price-no-older-than.ts
  10. 2 0
      apps/api-reference/src/apis/evm/get-price-unsafe.ts
  11. 1 0
      apps/api-reference/src/apis/evm/get-price.ts
  12. 6 9
      apps/api-reference/src/apis/evm/get-update-fee.tsx
  13. 1 0
      apps/api-reference/src/apis/evm/get-valid-time-period.ts
  14. 4 2
      apps/api-reference/src/apis/evm/parse-price-feed-updates-unique.tsx
  15. 4 2
      apps/api-reference/src/apis/evm/parse-price-feed-updates.tsx
  16. 4 2
      apps/api-reference/src/apis/evm/update-price-feeds-if-necessary.tsx
  17. 3 2
      apps/api-reference/src/apis/evm/update-price-feeds.tsx
  18. 0 42
      apps/api-reference/src/app/price-feeds/[chain]/[method]/layout.tsx
  19. 13 18
      apps/api-reference/src/app/price-feeds/[chain]/[method]/page.tsx
  20. 0 22
      apps/api-reference/src/app/price-feeds/[chain]/layout.tsx
  21. 0 3
      apps/api-reference/src/app/price-feeds/page.mdx
  22. 16 6
      apps/api-reference/src/components/Amplitude/index.tsx
  23. 41 14
      apps/api-reference/src/components/Button/index.tsx
  24. 13 89
      apps/api-reference/src/components/Code/index.tsx
  25. 124 0
      apps/api-reference/src/components/Code/use-highlighted-code.tsx
  26. 17 9
      apps/api-reference/src/components/EvmApi/index.tsx
  27. 13 1
      apps/api-reference/src/components/EvmApi/parameter-input.tsx
  28. 1 3
      apps/api-reference/src/components/EvmApi/parameter.ts
  29. 11 4
      apps/api-reference/src/components/EvmApi/run-button.tsx
  30. 0 60
      apps/api-reference/src/components/EvmLayout/index.tsx
  31. 60 0
      apps/api-reference/src/components/EvmProvider/index.tsx
  32. 1 1
      apps/api-reference/src/components/Header/index.tsx
  33. 17 7
      apps/api-reference/src/components/Header/nav-link.tsx
  34. 5 5
      apps/api-reference/src/components/Home/index.tsx
  35. 5 22
      apps/api-reference/src/components/InlineLink/index.tsx
  36. 2 20
      apps/api-reference/src/components/MaxWidth/index.tsx
  37. 2 8
      apps/api-reference/src/components/Paragraph/index.tsx
  38. 17 7
      apps/api-reference/src/components/Root/index.tsx
  39. 77 20
      apps/api-reference/src/components/Sidebar/index.tsx
  40. 18 0
      apps/api-reference/src/components/Styled/index.tsx
  41. 12 0
      apps/api-reference/src/markdown-components.tsx
  42. 0 18
      apps/api-reference/src/mdx-components.tsx
  43. 42 1
      apps/api-reference/src/server-config.ts
  44. 1 1
      apps/api-reference/tailwind.config.ts
  45. 19 15
      flake.nix
  46. 62 386
      pnpm-lock.yaml

+ 1 - 0
apps/api-reference/.gitignore

@@ -0,0 +1 @@
+.env*.local

+ 2 - 6
apps/api-reference/next.config.js

@@ -1,6 +1,4 @@
-import withMDX from "@next/mdx";
-
-const config = {
+export default {
   reactStrictMode: true,
   reactStrictMode: true,
 
 
   pageExtensions: ["ts", "tsx", "mdx"],
   pageExtensions: ["ts", "tsx", "mdx"],
@@ -24,7 +22,7 @@ const config = {
     return config;
     return config;
   },
   },
 
 
-  transpilePackages: ["@pyth.network/*"],
+  transpilePackages: ["@pythnetwork/*"],
 
 
   headers: () => [
   headers: () => [
     {
     {
@@ -55,5 +53,3 @@ const config = {
     },
     },
   ],
   ],
 };
 };
-
-export default withMDX()(config);

+ 3 - 5
apps/api-reference/package.json

@@ -11,6 +11,7 @@
     "fix": "pnpm fix:format && pnpm fix:lint",
     "fix": "pnpm fix:format && pnpm fix:lint",
     "fix:format": "prettier --write .",
     "fix:format": "prettier --write .",
     "fix:lint": "eslint --fix .",
     "fix:lint": "eslint --fix .",
+    "pull:env": "VERCEL_ORG_ID=team_BKQrg3JJFLxZyTqpuYtIY0rj VERCEL_PROJECT_ID=prj_gbljYVzp0m5EpCuOF6nZpM4WMFM6 vercel env pull",
     "start:dev": "next dev",
     "start:dev": "next dev",
     "start:prod": "next start",
     "start:prod": "next start",
     "test": "tsc && jest",
     "test": "tsc && jest",
@@ -21,13 +22,10 @@
   },
   },
   "dependencies": {
   "dependencies": {
     "@amplitude/analytics-browser": "^2.9.0",
     "@amplitude/analytics-browser": "^2.9.0",
+    "@amplitude/plugin-autocapture-browser": "^0.9.0",
     "@floating-ui/react": "^0.26.17",
     "@floating-ui/react": "^0.26.17",
     "@headlessui/react": "^2.0.4",
     "@headlessui/react": "^2.0.4",
     "@heroicons/react": "^2.1.4",
     "@heroicons/react": "^2.1.4",
-    "@mdx-js/loader": "^3.0.1",
-    "@mdx-js/mdx": "^3.0.1",
-    "@mdx-js/react": "^3.0.1",
-    "@next/mdx": "^14.2.4",
     "@next/third-parties": "^14.2.4",
     "@next/third-parties": "^14.2.4",
     "@pythnetwork/pyth-sdk-solidity": "workspace:^",
     "@pythnetwork/pyth-sdk-solidity": "workspace:^",
     "@tanstack/react-query": "^5.45.1",
     "@tanstack/react-query": "^5.45.1",
@@ -39,6 +37,7 @@
     "pino": "^9.2.0",
     "pino": "^9.2.0",
     "react": "^18.3.1",
     "react": "^18.3.1",
     "react-dom": "^18.3.1",
     "react-dom": "^18.3.1",
+    "react-markdown": "^9.0.1",
     "shiki": "^1.7.0",
     "shiki": "^1.7.0",
     "viem": "^2.15.1",
     "viem": "^2.15.1",
     "wagmi": "^2.10.4",
     "wagmi": "^2.10.4",
@@ -53,7 +52,6 @@
     "@svgr/webpack": "^8.1.0",
     "@svgr/webpack": "^8.1.0",
     "@tailwindcss/forms": "^0.5.7",
     "@tailwindcss/forms": "^0.5.7",
     "@types/jest": "^29.5.12",
     "@types/jest": "^29.5.12",
-    "@types/mdx": "^2.0.13",
     "@types/node": "^20.14.6",
     "@types/node": "^20.14.6",
     "@types/react": "^18.3.3",
     "@types/react": "^18.3.3",
     "@types/react-dom": "^18.3.0",
     "@types/react-dom": "^18.3.0",

+ 1 - 0
apps/api-reference/public/.well-known/walletconnect.txt

@@ -0,0 +1 @@
+6486ae45-385f-4747-b8b5-885380fb4ec8=57ef2db8537f4094003f355e80ee6a28a072d4b95baba68db01bac4981ea0f1b

+ 4 - 15
apps/api-reference/src/apis/evm/common.ts

@@ -10,18 +10,14 @@ import {
 import { singletonArray, safeFetch } from "../../zod-utils";
 import { singletonArray, safeFetch } from "../../zod-utils";
 
 
 export const readApi = <ParameterName extends string>(
 export const readApi = <ParameterName extends string>(
-  spec: Omit<ReadApi<ParameterName>, "children" | "type"> & {
-    description: string;
-  },
+  spec: Omit<ReadApi<ParameterName>, "type">,
 ) => ({
 ) => ({
   ...spec,
   ...spec,
   type: EvmApiType.Read,
   type: EvmApiType.Read,
 });
 });
 
 
 export const writeApi = <ParameterName extends string>(
 export const writeApi = <ParameterName extends string>(
-  spec: Omit<WriteApi<ParameterName>, "children" | "type"> & {
-    description: string;
-  },
+  spec: Omit<WriteApi<ParameterName>, "type">,
 ) => ({
 ) => ({
   ...spec,
   ...spec,
   type: EvmApiType.Write,
   type: EvmApiType.Write,
@@ -34,19 +30,15 @@ export const ETHUSD =
 
 
 const HERMES_URL = "https://hermes.pyth.network";
 const HERMES_URL = "https://hermes.pyth.network";
 
 
-export const getLatestPriceFeed = async (feedId: string) => {
+export const getLatestPriceUpdate = async (feedId: string) => {
   const url = new URL("/v2/updates/price/latest", HERMES_URL);
   const url = new URL("/v2/updates/price/latest", HERMES_URL);
   url.searchParams.set("ids[]", feedId);
   url.searchParams.set("ids[]", feedId);
-  url.searchParams.set("target_chain", "evm");
-  url.searchParams.set("binary", "true");
   return safeFetch(priceFeedSchema, url);
   return safeFetch(priceFeedSchema, url);
 };
 };
 
 
 const priceFeedSchema = z.object({
 const priceFeedSchema = z.object({
   binary: z.object({
   binary: z.object({
-    data: singletonArray(z.string()).transform((value) =>
-      toZeroXPrefixedHex(value),
-    ),
+    data: singletonArray(z.string()).transform((value) => `0x${value}`),
   }),
   }),
   parsed: singletonArray(
   parsed: singletonArray(
     z.object({
     z.object({
@@ -57,9 +49,6 @@ const priceFeedSchema = z.object({
   ),
   ),
 });
 });
 
 
-const toZeroXPrefixedHex = (value: string) =>
-  `0x${Buffer.from(value, "base64").toString("hex")}`;
-
 export const solidity = <ParameterName extends string>(
 export const solidity = <ParameterName extends string>(
   code: string | ((params: Partial<Record<ParameterName, string>>) => string),
   code: string | ((params: Partial<Record<ParameterName, string>>) => string),
 ) => ({
 ) => ({

+ 2 - 0
apps/api-reference/src/apis/evm/get-ema-price-no-older-than.ts

@@ -6,6 +6,8 @@ import { ParameterType } from "../../components/EvmApi";
 
 
 export const getEmaPriceNoOlderThan = readApi<"id" | "age">({
 export const getEmaPriceNoOlderThan = readApi<"id" | "age">({
   name: "getEmaPriceNoOlderThan",
   name: "getEmaPriceNoOlderThan",
+  summary:
+    "Get the exponentially weighted moving average (EMA) price object with a published timestamp from before than `age` seconds in the past.",
   description: `
   description: `
 Get the latest exponentially-weighted moving average (EMA) price and confidence
 Get the latest exponentially-weighted moving average (EMA) price and confidence
 interval for the requested price feed id.  The price feed id is a 32-byte id
 interval for the requested price feed id.  The price feed id is a 32-byte id

+ 2 - 0
apps/api-reference/src/apis/evm/get-ema-price-unsafe.ts

@@ -6,6 +6,8 @@ import { ParameterType } from "../../components/EvmApi";
 
 
 export const getEmaPriceUnsafe = readApi<"id">({
 export const getEmaPriceUnsafe = readApi<"id">({
   name: "getEmaPriceUnsafe",
   name: "getEmaPriceUnsafe",
+  summary:
+    "Get the **last updated** exponentially weighted moving average (EMA) price object for the requested price feed ID. _Caution: This function may return a price arbitrarily in the past_",
   description: `
   description: `
 Get the latest exponentially-weighted moving average (EMA) price and confidence
 Get the latest exponentially-weighted moving average (EMA) price and confidence
 interval for the requested price feed id.  The price feed id is a 32-byte id
 interval for the requested price feed id.  The price feed id is a 32-byte id

+ 2 - 0
apps/api-reference/src/apis/evm/get-ema-price.ts

@@ -6,6 +6,8 @@ import { ParameterType } from "../../components/EvmApi";
 
 
 export const getEmaPrice = readApi<"id">({
 export const getEmaPrice = readApi<"id">({
   name: "getEmaPrice",
   name: "getEmaPrice",
+  summary:
+    "Get the **latest** exponentially weighted moving average (EMA) price object for the requested price feed ID.",
   description: `
   description: `
 Get the latest exponentially-weighted moving average (EMA) price and confidence
 Get the latest exponentially-weighted moving average (EMA) price and confidence
 interval for the requested price feed id.  The price feed id is a 32-byte id
 interval for the requested price feed id.  The price feed id is a 32-byte id

+ 2 - 0
apps/api-reference/src/apis/evm/get-price-no-older-than.ts

@@ -6,6 +6,8 @@ import { ParameterType } from "../../components/EvmApi";
 
 
 export const getPriceNoOlderThan = readApi<"id" | "age">({
 export const getPriceNoOlderThan = readApi<"id" | "age">({
   name: "getPriceNoOlderThan",
   name: "getPriceNoOlderThan",
+  summary:
+    "Get the price object with a published timestamp from before than `age` seconds in the past.",
   description: `
   description: `
 Get the latest price and confidence interval for the requested price feed id, if
 Get the latest price and confidence interval for the requested price feed id, if
 it has been updated sufficiently recently.  The price feed id is a 32-byte id
 it has been updated sufficiently recently.  The price feed id is a 32-byte id

+ 2 - 0
apps/api-reference/src/apis/evm/get-price-unsafe.ts

@@ -6,6 +6,8 @@ import { ParameterType } from "../../components/EvmApi";
 
 
 export const getPriceUnsafe = readApi<"id">({
 export const getPriceUnsafe = readApi<"id">({
   name: "getPriceUnsafe",
   name: "getPriceUnsafe",
+  summary:
+    "Get the **last updated** price object for the requested price feed ID. _Caution: This function may return a price from arbitrarily in the the past_",
   description: `
   description: `
 Get the latest price and confidence interval for the requested price feed id.
 Get the latest price and confidence interval for the requested price feed id.
 The price feed id is a 32-byte id written as a hexadecimal string; see the
 The price feed id is a 32-byte id written as a hexadecimal string; see the

+ 1 - 0
apps/api-reference/src/apis/evm/get-price.ts

@@ -6,6 +6,7 @@ import { ParameterType } from "../../components/EvmApi";
 
 
 export const getPrice = readApi<"id">({
 export const getPrice = readApi<"id">({
   name: "getPrice",
   name: "getPrice",
+  summary: "Get the **latest** price object for the requested price feed ID.",
   description: `
   description: `
 Get the latest price and confidence interval for the requested price feed id.
 Get the latest price and confidence interval for the requested price feed id.
 The price feed id is a 32-byte id written as a hexadecimal string; see the
 The price feed id is a 32-byte id written as a hexadecimal string; see the

+ 6 - 9
apps/api-reference/src/apis/evm/get-update-fee.tsx

@@ -5,15 +5,16 @@ import {
   readApi,
   readApi,
   BTCUSD,
   BTCUSD,
   ETHUSD,
   ETHUSD,
-  getLatestPriceFeed,
+  getLatestPriceUpdate,
   solidity,
   solidity,
   ethersJS,
   ethersJS,
 } from "./common";
 } from "./common";
 import { ParameterType } from "../../components/EvmApi";
 import { ParameterType } from "../../components/EvmApi";
-import { InlineLink } from "../../components/InlineLink";
 
 
 export const getUpdateFee = readApi<"updateData">({
 export const getUpdateFee = readApi<"updateData">({
   name: "getUpdateFee",
   name: "getUpdateFee",
+  summary:
+    "Get the fee required to update the on-chain price feeds with the provided `updateData`.",
   description: `
   description: `
 Get the fee required to update the on-chain price feeds with the provided
 Get the fee required to update the on-chain price feeds with the provided
 \`updateData\`.  The returned number of wei should be sent as the transaction
 \`updateData\`.  The returned number of wei should be sent as the transaction
@@ -24,12 +25,8 @@ can be retrieved from the [Hermes API](https://hermes.pyth.network/docs).
     {
     {
       name: "updateData",
       name: "updateData",
       type: ParameterType.HexArray,
       type: ParameterType.HexArray,
-      description: (
-        <>
-          The price updates that you would like to submit to{" "}
-          <InlineLink href="updatePriceFeeds">updatePriceFeeds</InlineLink>
-        </>
-      ),
+      description:
+        "The price updates that you would like to submit to [updatePriceFeeds](updatePriceFeeds).",
     },
     },
   ],
   ],
   examples: [
   examples: [
@@ -62,6 +59,6 @@ const [feeAmount] = await contract.getUpdateFee(updateData);
 });
 });
 
 
 const getParams = async (feedId: string) => {
 const getParams = async (feedId: string) => {
-  const feed = await getLatestPriceFeed(feedId);
+  const feed = await getLatestPriceUpdate(feedId);
   return { updateData: feed.binary.data };
   return { updateData: feed.binary.data };
 };
 };

+ 1 - 0
apps/api-reference/src/apis/evm/get-valid-time-period.ts

@@ -2,6 +2,7 @@ import { readApi, solidity, ethersJS } from "./common";
 
 
 export const getValidTimePeriod = readApi<never>({
 export const getValidTimePeriod = readApi<never>({
   name: "getValidTimePeriod",
   name: "getValidTimePeriod",
+  summary: "Get the default valid time period of price freshness in seconds.",
   description: `
   description: `
 Get the default valid time period in seconds.  This quantity is the maximum age
 Get the default valid time period in seconds.  This quantity is the maximum age
 of price updates returned by functions like [getPrice](getPrice) and
 of price updates returned by functions like [getPrice](getPrice) and

+ 4 - 2
apps/api-reference/src/apis/evm/parse-price-feed-updates-unique.tsx

@@ -4,7 +4,7 @@ import Eth from "cryptocurrency-icons/svg/color/eth.svg";
 import {
 import {
   BTCUSD,
   BTCUSD,
   ETHUSD,
   ETHUSD,
-  getLatestPriceFeed,
+  getLatestPriceUpdate,
   solidity,
   solidity,
   ethersJS,
   ethersJS,
   writeApi,
   writeApi,
@@ -15,6 +15,8 @@ export const parsePriceFeedUpdatesUnique = writeApi<
   "updateData" | "priceId" | "minPublishTime" | "maxPublishTime" | "fee"
   "updateData" | "priceId" | "minPublishTime" | "maxPublishTime" | "fee"
 >({
 >({
   name: "parsePriceFeedUpdatesUnique",
   name: "parsePriceFeedUpdatesUnique",
+  summary:
+    "Parse `updateData` to return the **first updated** prices if the prices are published within the given time range.",
   description: `
   description: `
 Parse \`updateData\` and return the price feeds for the given \`priceIds\`
 Parse \`updateData\` and return the price feeds for the given \`priceIds\`
 within, if they are all **the first updates** published between
 within, if they are all **the first updates** published between
@@ -114,7 +116,7 @@ const getParams = async (
     readContract: (name: string, args: unknown[]) => Promise<unknown>;
     readContract: (name: string, args: unknown[]) => Promise<unknown>;
   },
   },
 ) => {
 ) => {
-  const feed = await getLatestPriceFeed(priceId);
+  const feed = await getLatestPriceUpdate(priceId);
   const fee = await ctx.readContract("getUpdateFee", [[feed.binary.data]]);
   const fee = await ctx.readContract("getUpdateFee", [[feed.binary.data]]);
   if (typeof fee !== "bigint") {
   if (typeof fee !== "bigint") {
     throw new TypeError("Invalid fee");
     throw new TypeError("Invalid fee");

+ 4 - 2
apps/api-reference/src/apis/evm/parse-price-feed-updates.tsx

@@ -4,7 +4,7 @@ import Eth from "cryptocurrency-icons/svg/color/eth.svg";
 import {
 import {
   BTCUSD,
   BTCUSD,
   ETHUSD,
   ETHUSD,
-  getLatestPriceFeed,
+  getLatestPriceUpdate,
   solidity,
   solidity,
   ethersJS,
   ethersJS,
   writeApi,
   writeApi,
@@ -15,6 +15,8 @@ export const parsePriceFeedUpdates = writeApi<
   "updateData" | "priceId" | "minPublishTime" | "maxPublishTime" | "fee"
   "updateData" | "priceId" | "minPublishTime" | "maxPublishTime" | "fee"
 >({
 >({
   name: "parsePriceFeedUpdates",
   name: "parsePriceFeedUpdates",
+  summary:
+    "Parse `updateData` to return prices if the prices are published within the given time range.",
   description: `
   description: `
 Parse \`updateData\` and return the price feeds for the given \`priceIds\`
 Parse \`updateData\` and return the price feeds for the given \`priceIds\`
 within, if they are all published between \`minPublishTime\` and
 within, if they are all published between \`minPublishTime\` and
@@ -112,7 +114,7 @@ const getParams = async (
     readContract: (name: string, args: unknown[]) => Promise<unknown>;
     readContract: (name: string, args: unknown[]) => Promise<unknown>;
   },
   },
 ) => {
 ) => {
-  const feed = await getLatestPriceFeed(priceId);
+  const feed = await getLatestPriceUpdate(priceId);
   const fee = await ctx.readContract("getUpdateFee", [[feed.binary.data]]);
   const fee = await ctx.readContract("getUpdateFee", [[feed.binary.data]]);
   if (typeof fee !== "bigint") {
   if (typeof fee !== "bigint") {
     throw new TypeError("Invalid fee");
     throw new TypeError("Invalid fee");

+ 4 - 2
apps/api-reference/src/apis/evm/update-price-feeds-if-necessary.tsx

@@ -4,7 +4,7 @@ import Eth from "cryptocurrency-icons/svg/color/eth.svg";
 import {
 import {
   BTCUSD,
   BTCUSD,
   ETHUSD,
   ETHUSD,
-  getLatestPriceFeed,
+  getLatestPriceUpdate,
   solidity,
   solidity,
   ethersJS,
   ethersJS,
   writeApi,
   writeApi,
@@ -15,6 +15,8 @@ export const updatePriceFeedsIfNecessary = writeApi<
   "updateData" | "priceId" | "publishTime" | "fee"
   "updateData" | "priceId" | "publishTime" | "fee"
 >({
 >({
   name: "updatePriceFeedsIfNecessary",
   name: "updatePriceFeedsIfNecessary",
+  summary:
+    "Update the on-chain price feeds using the provided `updateData` only if the on-chain prices are older than the valid time period.",
   description: `
   description: `
 Update the on-chain price feeds using the provided \`updateData\` if the
 Update the on-chain price feeds using the provided \`updateData\` if the
 on-chain data is not sufficiently fresh.  The caller provides two matched
 on-chain data is not sufficiently fresh.  The caller provides two matched
@@ -107,7 +109,7 @@ const getParams = async (
     readContract: (name: string, args: unknown[]) => Promise<unknown>;
     readContract: (name: string, args: unknown[]) => Promise<unknown>;
   },
   },
 ) => {
 ) => {
-  const feed = await getLatestPriceFeed(priceId);
+  const feed = await getLatestPriceUpdate(priceId);
   const fee = await ctx.readContract("getUpdateFee", [[feed.binary.data]]);
   const fee = await ctx.readContract("getUpdateFee", [[feed.binary.data]]);
   if (typeof fee !== "bigint") {
   if (typeof fee !== "bigint") {
     throw new TypeError("Invalid fee");
     throw new TypeError("Invalid fee");

+ 3 - 2
apps/api-reference/src/apis/evm/update-price-feeds.tsx

@@ -4,7 +4,7 @@ import Eth from "cryptocurrency-icons/svg/color/eth.svg";
 import {
 import {
   BTCUSD,
   BTCUSD,
   ETHUSD,
   ETHUSD,
-  getLatestPriceFeed,
+  getLatestPriceUpdate,
   solidity,
   solidity,
   ethersJS,
   ethersJS,
   writeApi,
   writeApi,
@@ -13,6 +13,7 @@ import { ParameterType } from "../../components/EvmApi";
 
 
 export const updatePriceFeeds = writeApi<"updateData" | "fee">({
 export const updatePriceFeeds = writeApi<"updateData" | "fee">({
   name: "updatePriceFeeds",
   name: "updatePriceFeeds",
+  summary: "Update the on-chain price feeds using the provided `updateData`.",
   description: `
   description: `
 Update the on-chain price feeds using the provided \`updateData\`, which
 Update the on-chain price feeds using the provided \`updateData\`, which
 contains serialized and signed price update data from Pyth Network.  You can
 contains serialized and signed price update data from Pyth Network.  You can
@@ -81,7 +82,7 @@ const getParams = async (
     readContract: (name: string, args: unknown[]) => Promise<unknown>;
     readContract: (name: string, args: unknown[]) => Promise<unknown>;
   },
   },
 ) => {
 ) => {
-  const feed = await getLatestPriceFeed(feedId);
+  const feed = await getLatestPriceUpdate(feedId);
   const fee = await ctx.readContract("getUpdateFee", [[feed.binary.data]]);
   const fee = await ctx.readContract("getUpdateFee", [[feed.binary.data]]);
   if (typeof fee !== "bigint") {
   if (typeof fee !== "bigint") {
     throw new TypeError("Invalid fee");
     throw new TypeError("Invalid fee");

+ 0 - 42
apps/api-reference/src/app/price-feeds/[chain]/[method]/layout.tsx

@@ -1,42 +0,0 @@
-"use client";
-
-import { notFound } from "next/navigation";
-import type { ReactNode, ComponentProps } from "react";
-
-import * as apis from "../../../../apis";
-import { EvmApi } from "../../../../components/EvmApi";
-
-type Props = {
-  params: {
-    chain: string;
-    method: string;
-  };
-  children: ReactNode;
-};
-
-const Layout = ({ params, children }: Props) => {
-  const chain: (typeof apis)[keyof typeof apis] | undefined = isKeyOf(
-    params.chain,
-    apis,
-  )
-    ? // eslint-disable-next-line import/namespace
-      apis[params.chain]
-    : undefined;
-  const api =
-    chain && isKeyOf(params.method, chain) ? chain[params.method] : undefined;
-  if (api) {
-    return (
-      <EvmApi {...(api as unknown as ComponentProps<typeof EvmApi>)}>
-        {children}
-      </EvmApi>
-    );
-  } else {
-    notFound();
-  }
-};
-export default Layout;
-
-const isKeyOf = <T extends Record<string, unknown>>(
-  value: unknown,
-  obj: T,
-): value is keyof T => typeof value === "string" && value in obj;

+ 13 - 18
apps/api-reference/src/app/price-feeds/[chain]/[method]/page.tsx

@@ -1,9 +1,10 @@
-import { evaluate } from "@mdx-js/mdx";
+"use client";
+
 import { notFound } from "next/navigation";
 import { notFound } from "next/navigation";
-import * as runtime from "react/jsx-runtime";
+import type { ComponentProps } from "react";
 
 
 import * as apis from "../../../../apis";
 import * as apis from "../../../../apis";
-import { useMDXComponents } from "../../../../mdx-components";
+import { EvmApi } from "../../../../components/EvmApi";
 
 
 type Props = {
 type Props = {
   params: {
   params: {
@@ -12,17 +13,18 @@ type Props = {
   };
   };
 };
 };
 
 
-const Page = async ({ params }: Props) => {
-  const mdxComponents = useMDXComponents({});
-  // eslint-disable-next-line import/namespace
-  const chain = isKeyOf(params.chain, apis) ? apis[params.chain] : undefined;
+const Page = ({ params }: Props) => {
+  const chain: (typeof apis)[keyof typeof apis] | undefined = isKeyOf(
+    params.chain,
+    apis,
+  )
+    ? // eslint-disable-next-line import/namespace
+      apis[params.chain]
+    : undefined;
   const api =
   const api =
     chain && isKeyOf(params.method, chain) ? chain[params.method] : undefined;
     chain && isKeyOf(params.method, chain) ? chain[params.method] : undefined;
   if (api) {
   if (api) {
-    // @ts-expect-error for some reason these types aren't unifying, it's
-    // probably a dependency versioning issue
-    const { default: Description } = await evaluate(api.description, runtime);
-    return <Description components={mdxComponents} />;
+    return <EvmApi {...(api as unknown as ComponentProps<typeof EvmApi>)} />;
   } else {
   } else {
     notFound();
     notFound();
   }
   }
@@ -33,10 +35,3 @@ const isKeyOf = <T extends Record<string, unknown>>(
   value: unknown,
   value: unknown,
   obj: T,
   obj: T,
 ): value is keyof T => typeof value === "string" && value in obj;
 ): value is keyof T => typeof value === "string" && value in obj;
-
-export const generateStaticParams = () =>
-  Object.entries(apis).flatMap(([chain, methods]) =>
-    Object.keys(methods).map((method) => ({ chain, method })),
-  );
-
-export const dynamicParams = false;

+ 0 - 22
apps/api-reference/src/app/price-feeds/[chain]/layout.tsx

@@ -1,22 +0,0 @@
-import type { ReactNode } from "react";
-
-import { EvmLayout } from "../../../components/EvmLayout";
-
-type Props = {
-  params: {
-    chain: string;
-  };
-  children: ReactNode;
-};
-
-const Layout = ({ params, children }: Props) => {
-  switch (params.chain) {
-    case "evm": {
-      return <EvmLayout>{children}</EvmLayout>;
-    }
-    default: {
-      return children;
-    }
-  }
-};
-export default Layout;

+ 0 - 3
apps/api-reference/src/app/price-feeds/page.mdx

@@ -1,3 +0,0 @@
-# Price Feeds
-
-Select an API from the side bar to get started!

+ 16 - 6
apps/api-reference/src/components/Amplitude/index.tsx

@@ -1,16 +1,26 @@
 "use client";
 "use client";
 
 
-import { init } from "@amplitude/analytics-browser";
-import { useEffect } from "react";
+import * as amplitude from "@amplitude/analytics-browser";
+import { autocapturePlugin } from "@amplitude/plugin-autocapture-browser";
+import { useEffect, useRef } from "react";
 
 
 type Props = {
 type Props = {
-  key: string;
+  apiKey: string | undefined;
 };
 };
 
 
-export const Amplitude = ({ key }: Props) => {
+export const Amplitude = ({ apiKey }: Props) => {
+  const amplitudeInitialized = useRef(false);
+
   useEffect(() => {
   useEffect(() => {
-    init(key);
-  }, [key]);
+    if (!amplitudeInitialized.current && apiKey) {
+      amplitude.add(autocapturePlugin());
+      amplitude.init(apiKey, {
+        defaultTracking: true,
+      });
+      amplitudeInitialized.current = true;
+    }
+  }, [apiKey]);
+
   // eslint-disable-next-line unicorn/no-null
   // eslint-disable-next-line unicorn/no-null
   return null;
   return null;
 };
 };

+ 41 - 14
apps/api-reference/src/components/Button/index.tsx

@@ -5,16 +5,24 @@ import {
   type ComponentProps,
   type ComponentProps,
   type ElementType,
   type ElementType,
   type MouseEvent,
   type MouseEvent,
+  type CSSProperties,
   useState,
   useState,
   useCallback,
   useCallback,
-  type CSSProperties,
 } from "react";
 } from "react";
 
 
+const DEFAULT_GRADIENT_SIZE = "30rem";
+
 type ButtonProps<T extends ElementType> = Omit<ComponentProps<T>, "as"> & {
 type ButtonProps<T extends ElementType> = Omit<ComponentProps<T>, "as"> & {
   as?: T;
   as?: T;
   loading?: boolean | undefined;
   loading?: boolean | undefined;
   gradient?: boolean | undefined;
   gradient?: boolean | undefined;
-};
+} & (
+    | { gradient?: false | undefined }
+    | {
+        gradient: true;
+        gradientSize?: string | undefined;
+      }
+  );
 
 
 export const Button = <T extends ElementType>({
 export const Button = <T extends ElementType>({
   as,
   as,
@@ -22,6 +30,7 @@ export const Button = <T extends ElementType>({
   loading,
   loading,
   disabled,
   disabled,
   gradient,
   gradient,
+  children,
   ...props
   ...props
 }: ButtonProps<T>) => {
 }: ButtonProps<T>) => {
   const Component = as ?? "button";
   const Component = as ?? "button";
@@ -38,23 +47,15 @@ export const Button = <T extends ElementType>({
     <Component
     <Component
       disabled={loading === true || disabled === true}
       disabled={loading === true || disabled === true}
       onMouseMove={updateMouse}
       onMouseMove={updateMouse}
-      style={
-        gradient
-          ? ({
-              "--gradient-left": `${mouse.x.toString()}px`,
-              "--gradient-top": `${mouse.y.toString()}px`,
-            } as CSSProperties)
-          : {}
-      }
       className={clsx(
       className={clsx(
-        "relative overflow-hidden rounded-lg border text-sm font-medium transition-all duration-300",
+        "group relative overflow-hidden rounded-lg border text-sm font-medium transition-all duration-300",
         {
         {
           "border-neutral-400 hover:border-pythpurple-600 hover:shadow-md dark:border-neutral-600 dark:hover:border-pythpurple-400 dark:hover:shadow-white/20":
           "border-neutral-400 hover:border-pythpurple-600 hover:shadow-md dark:border-neutral-600 dark:hover:border-pythpurple-400 dark:hover:shadow-white/20":
             !loading && !disabled,
             !loading && !disabled,
-          "before:absolute before:left-[var(--gradient-left)] before:top-[var(--gradient-top)] before:-ml-[20rem] before:-mt-[20rem] before:block before:size-[40rem] before:scale-0 before:bg-gradient-radial before:from-pythpurple-400/30 before:to-70% before:opacity-50 before:transition before:duration-500 hover:before:scale-100 hover:before:opacity-100 dark:before:from-pythpurple-600/30":
-            gradient && !loading && !disabled,
           "bg-pythpurple-600/5 hover:bg-pythpurple-600/15 dark:bg-pythpurple-400/5 dark:hover:bg-pythpurple-400/15":
           "bg-pythpurple-600/5 hover:bg-pythpurple-600/15 dark:bg-pythpurple-400/5 dark:hover:bg-pythpurple-400/15":
             !gradient && !loading && !disabled,
             !gradient && !loading && !disabled,
+          "active:bg-pythpurple-400/10 dark:active:bg-pythpurple-600/10":
+            gradient && !loading && !disabled,
           "border-neutral-200 bg-neutral-100 text-neutral-400 dark:border-neutral-800 dark:bg-neutral-900 dark:text-neutral-500":
           "border-neutral-200 bg-neutral-100 text-neutral-400 dark:border-neutral-800 dark:bg-neutral-900 dark:text-neutral-500":
             loading === true || disabled === true,
             loading === true || disabled === true,
           "cursor-not-allowed": disabled === true && loading !== true,
           "cursor-not-allowed": disabled === true && loading !== true,
@@ -63,6 +64,32 @@ export const Button = <T extends ElementType>({
         className,
         className,
       )}
       )}
       {...props}
       {...props}
-    />
+    >
+      {gradient && !loading && !disabled && (
+        <Gradient size={props.gradientSize} x={mouse.x} y={mouse.y} />
+      )}
+      {children}
+    </Component>
   );
   );
 };
 };
+
+type GradientProps = {
+  x: number;
+  y: number;
+  size?: string | undefined;
+};
+
+const Gradient = ({ size = DEFAULT_GRADIENT_SIZE, x, y }: GradientProps) => (
+  <div
+    style={
+      {
+        "--gradient-left": `${x.toString()}px`,
+        "--gradient-top": `${y.toString()}px`,
+        "--gradient-size": size,
+      } as CSSProperties
+    }
+    className="pointer-events-none absolute left-0 top-0 -z-10 ml-[calc(-1_*_var(--gradient-size)_/_2)] mt-[calc(-1_*_var(--gradient-size)_/_2)] block size-[var(--gradient-size)] translate-x-[var(--gradient-left)] translate-y-[var(--gradient-top)]"
+  >
+    <div className="size-full scale-0 bg-gradient-radial from-pythpurple-400 to-70% opacity-10 transition duration-500 group-hover:scale-100 group-hover:opacity-30 group-active:scale-150 group-active:opacity-40 dark:from-pythpurple-600" />
+  </div>
+);

+ 13 - 89
apps/api-reference/src/components/Code/index.tsx

@@ -2,11 +2,12 @@ import { Transition } from "@headlessui/react";
 import { ClipboardDocumentIcon, CheckIcon } from "@heroicons/react/24/outline";
 import { ClipboardDocumentIcon, CheckIcon } from "@heroicons/react/24/outline";
 import clsx from "clsx";
 import clsx from "clsx";
 import { useMemo, useCallback, type HTMLAttributes } from "react";
 import { useMemo, useCallback, type HTMLAttributes } from "react";
-import { useRef, useEffect, useState } from "react";
+import { useEffect, useState } from "react";
 import type { OffsetOrPosition } from "shiki";
 import type { OffsetOrPosition } from "shiki";
 
 
-import type { Highlighter, SupportedLanguage } from "./shiki";
+import type { SupportedLanguage } from "./shiki";
 import style from "./style.module.css";
 import style from "./style.module.css";
+import { useHighlightedCode } from "./use-highlighted-code";
 import { getLogger } from "../../browser-logger";
 import { getLogger } from "../../browser-logger";
 import { Button } from "../Button";
 import { Button } from "../Button";
 
 
@@ -87,8 +88,17 @@ const CopyButton = ({ children, className, ...props }: CopyButtonProps) => {
       className={clsx("bg-neutral-100 dark:bg-neutral-800", className)}
       className={clsx("bg-neutral-100 dark:bg-neutral-800", className)}
       {...props}
       {...props}
     >
     >
+      <Button
+        onClick={copy}
+        className="rounded-md p-2 text-neutral-800 dark:text-neutral-300"
+      >
+        <ClipboardDocumentIcon className="size-4" />
+        <div className="sr-only">Copy code to clipboaord</div>
+      </Button>
       <Transition
       <Transition
         show={isCopied}
         show={isCopied}
+        as="div"
+        className="absolute inset-0 rounded-md border border-green-500 bg-green-50 p-2 text-green-700 dark:border-green-700 dark:bg-green-950 dark:text-green-500"
         enter="transition-opacity duration-150"
         enter="transition-opacity duration-150"
         enterFrom="opacity-0"
         enterFrom="opacity-0"
         enterTo="opacity-100"
         enterTo="opacity-100"
@@ -96,17 +106,8 @@ const CopyButton = ({ children, className, ...props }: CopyButtonProps) => {
         leaveFrom="opacity-100"
         leaveFrom="opacity-100"
         leaveTo="opacity-0"
         leaveTo="opacity-0"
       >
       >
-        <div className="absolute size-full rounded-md border border-green-500 bg-green-50 p-2 text-green-700 dark:border-green-700 dark:bg-green-950 dark:text-green-500">
-          <CheckIcon className="size-4 stroke-2" />
-        </div>
+        <CheckIcon className="size-4 stroke-2" />
       </Transition>
       </Transition>
-      <Button
-        onClick={copy}
-        className="rounded-md p-2 text-neutral-800 dark:text-neutral-300"
-      >
-        <ClipboardDocumentIcon className="size-4" />
-        <div className="sr-only">Copy code to clipboaord</div>
-      </Button>
     </div>
     </div>
   );
   );
 };
 };
@@ -141,80 +142,3 @@ const HighlightedCode = ({
     </div>
     </div>
   );
   );
 };
 };
-
-const useHighlightedCode = (
-  language: SupportedLanguage,
-  code: string,
-  dimRange?: readonly [OffsetOrPosition, OffsetOrPosition] | undefined,
-) => {
-  const [highlightedCode, setHighlightedCode] = useState<string | undefined>(
-    undefined,
-  );
-  const highlighter = useRef<Highlighter | undefined>(undefined);
-  const decorations = useMemo(
-    () =>
-      dimRange
-        ? [
-            {
-              start: dimRange[0],
-              end: dimRange[1],
-              properties: {
-                class: "opacity-40 group-hover:opacity-100 transition",
-              },
-            },
-          ]
-        : undefined,
-    [dimRange],
-  );
-
-  useEffect(() => {
-    if (highlighter.current) {
-      setHighlightedCode(
-        highlighter.current.highlight(language, code, { decorations }),
-      );
-      return;
-    } else {
-      const { cancel, load } = createShikiLoader();
-      load()
-        .then((newHighlighter) => {
-          if (newHighlighter) {
-            highlighter.current = newHighlighter;
-            setHighlightedCode(
-              newHighlighter.highlight(language, code, { decorations }),
-            );
-          }
-        })
-        .catch((error: unknown) => {
-          // TODO report these errors somewhere
-          getLogger().error(error);
-        });
-      return cancel;
-    }
-  }, [code, language, decorations]);
-
-  return highlightedCode;
-};
-
-const createShikiLoader = () => {
-  let cancelled = false;
-  return {
-    load: async () => {
-      const { getHighlighter } = await import("./shiki");
-      if (cancelled) {
-        return;
-      } else {
-        const highlighter = await getHighlighter();
-        // Typescript narrows optimistically, meaning that by the time the code
-        // reaches this point, typescript things that `cancelled` can only be
-        // `false`.  However, that's not actually true and some other code could
-        // have called `cancel` during the `await` and flipped `cancelled` to
-        // false, so we should check it here too.
-        // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
-        return cancelled ? undefined : highlighter;
-      }
-    },
-    cancel: () => {
-      cancelled = true;
-    },
-  };
-};

+ 124 - 0
apps/api-reference/src/components/Code/use-highlighted-code.tsx

@@ -0,0 +1,124 @@
+"use client";
+
+import {
+  type ReactNode,
+  type MutableRefObject,
+  createContext,
+  useContext,
+  useState,
+  useMemo,
+  useEffect,
+  useRef,
+} from "react";
+import type { OffsetOrPosition } from "shiki";
+
+import type { Highlighter, SupportedLanguage } from "./shiki";
+import { getLogger } from "../../browser-logger";
+
+const HighlighterContext = createContext<
+  undefined | MutableRefObject<undefined | Highlighter>
+>(undefined);
+
+export const HighlighterProvider = ({
+  children,
+}: {
+  children: ReactNode | ReactNode[];
+}) => {
+  const highlighterRef = useRef<undefined | Highlighter>();
+  return (
+    <HighlighterContext.Provider value={highlighterRef}>
+      {children}
+    </HighlighterContext.Provider>
+  );
+};
+
+const useHighlighter = () => {
+  const highlighter = useContext(HighlighterContext);
+
+  if (highlighter === undefined) {
+    throw new Error(
+      "The `HighlighterProvider` component must be used to initialized the highlighter!",
+    );
+  }
+
+  return highlighter;
+};
+
+export const useHighlightedCode = (
+  language: SupportedLanguage,
+  code: string,
+  dimRange?: readonly [OffsetOrPosition, OffsetOrPosition] | undefined,
+) => {
+  const highlighter = useHighlighter();
+  const decorations = useMemo(
+    () =>
+      dimRange
+        ? [
+            {
+              start: dimRange[0],
+              end: dimRange[1],
+              properties: {
+                class: "opacity-40 group-hover:opacity-100 transition",
+              },
+            },
+          ]
+        : undefined,
+    [dimRange],
+  );
+  const [highlightedCode, setHighlightedCode] = useState<string | undefined>(
+    highlighter.current
+      ? highlighter.current.highlight(language, code, { decorations })
+      : undefined,
+  );
+
+  useEffect(() => {
+    if (highlighter.current) {
+      setHighlightedCode(
+        highlighter.current.highlight(language, code, { decorations }),
+      );
+      return;
+    } else {
+      const { cancel, load } = createShikiLoader();
+      load()
+        .then((newHighlighter) => {
+          if (newHighlighter) {
+            highlighter.current = newHighlighter;
+            setHighlightedCode(
+              newHighlighter.highlight(language, code, { decorations }),
+            );
+          }
+        })
+        .catch((error: unknown) => {
+          // TODO report these errors somewhere
+          getLogger().error(error);
+        });
+      return cancel;
+    }
+  }, [code, language, decorations, highlighter]);
+
+  return highlightedCode;
+};
+
+const createShikiLoader = () => {
+  let cancelled = false;
+  return {
+    load: async () => {
+      const { getHighlighter } = await import("./shiki");
+      if (cancelled) {
+        return;
+      } else {
+        const highlighter = await getHighlighter();
+        // Typescript narrows optimistically, meaning that by the time the code
+        // reaches this point, typescript things that `cancelled` can only be
+        // `false`.  However, that's not actually true and some other code could
+        // have called `cancel` during the `await` and flipped `cancelled` to
+        // false, so we should check it here too.
+        // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+        return cancelled ? undefined : highlighter;
+      }
+    },
+    cancel: () => {
+      cancelled = true;
+    },
+  };
+};

+ 17 - 9
apps/api-reference/src/components/EvmApi/index.tsx

@@ -14,23 +14,25 @@ import PythAbi from "@pythnetwork/pyth-sdk-solidity/abis/IPyth.json";
 import PythErrorsAbi from "@pythnetwork/pyth-sdk-solidity/abis/PythErrors.json";
 import PythErrorsAbi from "@pythnetwork/pyth-sdk-solidity/abis/PythErrors.json";
 import { ChainIcon } from "connectkit";
 import { ChainIcon } from "connectkit";
 import {
 import {
-  type ReactNode,
   type Dispatch,
   type Dispatch,
   type SetStateAction,
   type SetStateAction,
   type ComponentProps,
   type ComponentProps,
+  type ElementType,
+  type SVGAttributes,
   useState,
   useState,
   useCallback,
   useCallback,
   useMemo,
   useMemo,
-  type ComponentType,
-  type SVGAttributes,
 } from "react";
 } from "react";
+import Markdown from "react-markdown";
 import { useSwitchChain, useChainId, useConfig } from "wagmi";
 import { useSwitchChain, useChainId, useConfig } from "wagmi";
 import { readContract } from "wagmi/actions";
 import { readContract } from "wagmi/actions";
 
 
 import { getContractAddress } from "./networks";
 import { getContractAddress } from "./networks";
-import { type Parameter } from "./parameter";
+import type { Parameter } from "./parameter";
 import { ParameterInput } from "./parameter-input";
 import { ParameterInput } from "./parameter-input";
 import { type EvmApiType, RunButton } from "./run-button";
 import { type EvmApiType, RunButton } from "./run-button";
+import { getLogger } from "../../browser-logger";
+import { MARKDOWN_COMPONENTS } from "../../markdown-components";
 import { useIsMounted } from "../../use-is-mounted";
 import { useIsMounted } from "../../use-is-mounted";
 import { type SupportedLanguage, Code } from "../Code";
 import { type SupportedLanguage, Code } from "../Code";
 import { ErrorTooltip } from "../ErrorTooltip";
 import { ErrorTooltip } from "../ErrorTooltip";
@@ -48,7 +50,8 @@ type Props<ParameterName extends string> =
 
 
 type Common<ParameterName extends string> = {
 type Common<ParameterName extends string> = {
   name: (typeof PythAbi)[number]["name"];
   name: (typeof PythAbi)[number]["name"];
-  children: ReactNode;
+  summary: string;
+  description: string;
   parameters: Parameter<ParameterName>[];
   parameters: Parameter<ParameterName>[];
   examples: Example<ParameterName>[];
   examples: Example<ParameterName>[];
   code: CodeSample<ParameterName>[];
   code: CodeSample<ParameterName>[];
@@ -65,7 +68,7 @@ export type WriteApi<ParameterName extends string> = Common<ParameterName> & {
 
 
 type Example<ParameterName extends string> = {
 type Example<ParameterName extends string> = {
   name: string;
   name: string;
-  icon?: ComponentType<SVGAttributes<SVGSVGElement>>;
+  icon?: ElementType<SVGAttributes<SVGSVGElement>>;
   parameters: ValueOrFunctionOrAsyncFunction<Record<ParameterName, string>>;
   parameters: ValueOrFunctionOrAsyncFunction<Record<ParameterName, string>>;
 };
 };
 
 
@@ -100,7 +103,8 @@ export type NetworkInfo = {
 
 
 export const EvmApi = <ParameterName extends string>({
 export const EvmApi = <ParameterName extends string>({
   name,
   name,
-  children,
+  summary,
+  description,
   parameters,
   parameters,
   code,
   code,
   examples,
   examples,
@@ -125,11 +129,14 @@ export const EvmApi = <ParameterName extends string>({
   return (
   return (
     <div className="gap-x-20 lg:grid lg:grid-cols-[2fr_1fr]">
     <div className="gap-x-20 lg:grid lg:grid-cols-[2fr_1fr]">
       <h1 className="col-span-2 mb-6 font-mono text-4xl font-medium">{name}</h1>
       <h1 className="col-span-2 mb-6 font-mono text-4xl font-medium">{name}</h1>
+      <div className="col-span-2 mb-6 opacity-60">
+        <Markdown components={MARKDOWN_COMPONENTS}>{summary}</Markdown>
+      </div>
       <section>
       <section>
         <h2 className="mb-4 border-b border-neutral-200 text-2xl/loose font-medium dark:border-neutral-800">
         <h2 className="mb-4 border-b border-neutral-200 text-2xl/loose font-medium dark:border-neutral-800">
           Description
           Description
         </h2>
         </h2>
-        {children}
+        <Markdown components={MARKDOWN_COMPONENTS}>{description}</Markdown>
       </section>
       </section>
       <section className="flex flex-col">
       <section className="flex flex-col">
         <h2 className="mb-4 border-b border-neutral-200 text-2xl/loose font-medium dark:border-neutral-800">
         <h2 className="mb-4 border-b border-neutral-200 text-2xl/loose font-medium dark:border-neutral-800">
@@ -282,7 +289,8 @@ const Example = <ParameterName extends string>({
           .then((paramsResolved) => {
           .then((paramsResolved) => {
             setParamValues(paramsResolved);
             setParamValues(paramsResolved);
           })
           })
-          .catch(() => {
+          .catch((error_: unknown) => {
+            getLogger().error(error_);
             setError(
             setError(
               "An error occurred while fetching data for this example, please try again",
               "An error occurred while fetching data for this example, please try again",
             );
             );

+ 13 - 1
apps/api-reference/src/components/EvmApi/parameter-input.tsx

@@ -2,11 +2,13 @@ import {
   type ChangeEvent,
   type ChangeEvent,
   type Dispatch,
   type Dispatch,
   type SetStateAction,
   type SetStateAction,
+  Fragment,
   useState,
   useState,
   useCallback,
   useCallback,
   useMemo,
   useMemo,
   useEffect,
   useEffect,
 } from "react";
 } from "react";
+import Markdown from "react-markdown";
 
 
 import {
 import {
   type Parameter,
   type Parameter,
@@ -14,6 +16,7 @@ import {
   isValid,
   isValid,
   getValidationError,
   getValidationError,
 } from "./parameter";
 } from "./parameter";
+import { MARKDOWN_COMPONENTS } from "../../markdown-components";
 import { Input } from "../Input";
 import { Input } from "../Input";
 
 
 type ParameterProps<ParameterName extends string> = {
 type ParameterProps<ParameterName extends string> = {
@@ -39,7 +42,16 @@ export const ParameterInput = <ParameterName extends string>({
     <Input
     <Input
       validationError={validationError}
       validationError={validationError}
       label={spec.name}
       label={spec.name}
-      description={spec.description}
+      description={
+        <Markdown
+          components={{
+            ...MARKDOWN_COMPONENTS,
+            p: ({ children }) => <Fragment>{children}</Fragment>,
+          }}
+        >
+          {spec.description}
+        </Markdown>
+      }
       placeholder={PLACEHOLDERS[spec.type]}
       placeholder={PLACEHOLDERS[spec.type]}
       required={true}
       required={true}
       value={internalValue}
       value={internalValue}

+ 1 - 3
apps/api-reference/src/components/EvmApi/parameter.ts

@@ -1,9 +1,7 @@
-import type { ReactNode } from "react";
-
 export type Parameter<Name extends string> = {
 export type Parameter<Name extends string> = {
   name: Name;
   name: Name;
   type: ParameterType;
   type: ParameterType;
-  description: ReactNode;
+  description: string;
 };
 };
 
 
 export enum ParameterType {
 export enum ParameterType {

+ 11 - 4
apps/api-reference/src/components/EvmApi/run-button.tsx

@@ -6,7 +6,7 @@ import PythErrorsAbi from "@pythnetwork/pyth-sdk-solidity/abis/PythErrors.json";
 import { ConnectKitButton, Avatar } from "connectkit";
 import { ConnectKitButton, Avatar } from "connectkit";
 import { useCallback, useMemo, useState } from "react";
 import { useCallback, useMemo, useState } from "react";
 import { useAccount, useConfig } from "wagmi";
 import { useAccount, useConfig } from "wagmi";
-import { readContract, writeContract } from "wagmi/actions";
+import { readContract, simulateContract, writeContract } from "wagmi/actions";
 
 
 import { getContractAddress } from "./networks";
 import { getContractAddress } from "./networks";
 import { type Parameter, TRANSFORMS } from "./parameter";
 import { type Parameter, TRANSFORMS } from "./parameter";
@@ -175,7 +175,7 @@ const useRunButton = <ParameterName extends string>({
             })
             })
             .catch((error: unknown) => {
             .catch((error: unknown) => {
               setModalContents({
               setModalContents({
-                error: error,
+                error,
                 parameters: paramValues,
                 parameters: paramValues,
                 networkName,
                 networkName,
               });
               });
@@ -193,7 +193,14 @@ const useRunButton = <ParameterName extends string>({
             });
             });
             setStatus(Status.ShowingResults);
             setStatus(Status.ShowingResults);
           } else {
           } else {
-            writeContract(config, { abi, address, functionName, args, value })
+            simulateContract(config, {
+              abi,
+              address,
+              functionName,
+              args,
+              value,
+            })
+              .then(({ request }) => writeContract(config, request))
               .then((result) => {
               .then((result) => {
                 setModalContents({
                 setModalContents({
                   result,
                   result,
@@ -203,7 +210,7 @@ const useRunButton = <ParameterName extends string>({
               })
               })
               .catch((error: unknown) => {
               .catch((error: unknown) => {
                 setModalContents({
                 setModalContents({
-                  error: error,
+                  error,
                   parameters: paramValues,
                   parameters: paramValues,
                   networkName,
                   networkName,
                 });
                 });

+ 0 - 60
apps/api-reference/src/components/EvmLayout/index.tsx

@@ -1,60 +0,0 @@
-"use client";
-
-import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
-import { ConnectKitProvider, getDefaultConfig } from "connectkit";
-import { useTheme } from "next-themes";
-import type { ReactNode } from "react";
-import { WagmiProvider, createConfig, http, useChainId } from "wagmi";
-import { arbitrum, avalanche, mainnet, sepolia } from "wagmi/chains";
-
-import { metadata } from "../../metadata";
-
-const config = createConfig(
-  /* @ts-expect-error connectkit's types don't unify with wagmi's types using the exactOptionalPropertyTypes typescript setting */
-  getDefaultConfig({
-    chains: [mainnet, avalanche, arbitrum, sepolia],
-    transports: {
-      [mainnet.id]: http(),
-      [avalanche.id]: http(),
-      [arbitrum.id]: http(),
-      [sepolia.id]: http(),
-    },
-    appName: metadata.applicationName,
-    appDescription: metadata.description,
-    appUrl: metadata.metadataBase.toString(),
-    appIcon: metadata.icons.apple.url,
-  }),
-);
-
-const queryClient = new QueryClient();
-
-type EvmLayoutProps = {
-  children: ReactNode;
-};
-
-export const EvmLayout = ({ children }: EvmLayoutProps) => {
-  return (
-    <WagmiProvider config={config}>
-      <QueryClientProvider client={queryClient}>
-        <ConnectKitProviderWrapper>{children}</ConnectKitProviderWrapper>
-      </QueryClientProvider>
-    </WagmiProvider>
-  );
-};
-
-const ConnectKitProviderWrapper = ({ children }: { children: ReactNode }) => {
-  const { resolvedTheme } = useTheme();
-  const chainId = useChainId();
-
-  return (
-    <ConnectKitProvider
-      mode={resolvedTheme as "light" | "dark"}
-      options={{ initialChainId: chainId }}
-      customTheme={{
-        "--ck-font-family": "var(--font-sans)",
-      }}
-    >
-      {children}
-    </ConnectKitProvider>
-  );
-};

+ 60 - 0
apps/api-reference/src/components/EvmProvider/index.tsx

@@ -0,0 +1,60 @@
+"use client";
+
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import { ConnectKitProvider, getDefaultConfig } from "connectkit";
+import { useTheme } from "next-themes";
+import type { ReactNode } from "react";
+import { WagmiProvider, createConfig, http, useChainId } from "wagmi";
+import { arbitrum, avalanche, mainnet, sepolia } from "wagmi/chains";
+
+import { metadata } from "../../metadata";
+
+type EvmProviderProps = {
+  children: ReactNode;
+  walletConnectProjectId: string;
+};
+
+export const EvmProvider = ({
+  children,
+  walletConnectProjectId,
+}: EvmProviderProps) => (
+  <WagmiProvider
+    config={createConfig(
+      getDefaultConfig({
+        chains: [mainnet, avalanche, arbitrum, sepolia],
+        transports: {
+          [mainnet.id]: http(),
+          [avalanche.id]: http(),
+          [arbitrum.id]: http(),
+          [sepolia.id]: http(),
+        },
+        walletConnectProjectId,
+        appName: metadata.applicationName,
+        appDescription: metadata.description,
+        appUrl: metadata.metadataBase.toString(),
+        appIcon: metadata.icons.apple.url,
+      }),
+    )}
+  >
+    <QueryClientProvider client={new QueryClient()}>
+      <ConnectKitProviderWrapper>{children}</ConnectKitProviderWrapper>
+    </QueryClientProvider>
+  </WagmiProvider>
+);
+
+const ConnectKitProviderWrapper = ({ children }: { children: ReactNode }) => {
+  const { resolvedTheme } = useTheme();
+  const chainId = useChainId();
+
+  return (
+    <ConnectKitProvider
+      mode={resolvedTheme as "light" | "dark"}
+      options={{ initialChainId: chainId }}
+      customTheme={{
+        "--ck-font-family": "var(--font-sans)",
+      }}
+    >
+      {children}
+    </ConnectKitProvider>
+  );
+};

+ 1 - 1
apps/api-reference/src/components/Header/index.tsx

@@ -31,7 +31,7 @@ export const Header = ({
           >
           >
             <ul className="contents">
             <ul className="contents">
               <li className="contents">
               <li className="contents">
-                <NavLink href="/price-feeds">Price Feeds</NavLink>
+                <NavLink href="/price-feeds/evm/getPrice">Price Feeds</NavLink>
               </li>
               </li>
               <li className="contents">
               <li className="contents">
                 <NavLink href="/benchmarks">Benchmarks</NavLink>
                 <NavLink href="/benchmarks">Benchmarks</NavLink>

+ 17 - 7
apps/api-reference/src/components/Header/nav-link.tsx

@@ -3,16 +3,18 @@
 import clsx from "clsx";
 import clsx from "clsx";
 import Link from "next/link";
 import Link from "next/link";
 import { useSelectedLayoutSegment } from "next/navigation";
 import { useSelectedLayoutSegment } from "next/navigation";
-import type { ComponentProps } from "react";
+import { type ComponentProps, useMemo } from "react";
 
 
 const baseClasses = "font-semibold text-sm py-2 px-3";
 const baseClasses = "font-semibold text-sm py-2 px-3";
 
 
-export const NavLink = ({
-  className,
-  ...props
-}: ComponentProps<typeof Link>) => {
-  const segment = useSelectedLayoutSegment();
-  return segment && `/${segment}` === props.href ? (
+type NavLinkProps = Omit<ComponentProps<typeof Link>, "href"> & {
+  href: string;
+};
+
+export const NavLink = ({ className, ...props }: NavLinkProps) => {
+  const isCurrent = useIsCurrent(props.href);
+
+  return isCurrent ? (
     <span
     <span
       className={clsx(
       className={clsx(
         "text-pythpurple-600 dark:text-pythpurple-400",
         "text-pythpurple-600 dark:text-pythpurple-400",
@@ -33,3 +35,11 @@ export const NavLink = ({
     />
     />
   );
   );
 };
 };
+
+const useIsCurrent = (href: string) => {
+  const selectedSegment = useSelectedLayoutSegment();
+  return useMemo(
+    () => href.toString().split("/")[1] === selectedSegment,
+    [href, selectedSegment],
+  );
+};

+ 5 - 5
apps/api-reference/src/components/Home/index.tsx

@@ -1,5 +1,5 @@
 import Link from "next/link";
 import Link from "next/link";
-import type { ComponentType, SVGProps } from "react";
+import type { ElementType, SVGProps } from "react";
 
 
 import Benchmarks from "./benchmarks.svg";
 import Benchmarks from "./benchmarks.svg";
 import Entropy from "./entropy.svg";
 import Entropy from "./entropy.svg";
@@ -21,7 +21,7 @@ export const Home = () => (
           <li className="contents">
           <li className="contents">
             <ProductLink
             <ProductLink
               icon={PriceFeeds}
               icon={PriceFeeds}
-              href="/price-feeds"
+              href="/price-feeds/evm/getPrice"
               name="Price Feeds"
               name="Price Feeds"
             >
             >
               Fetch real-time low-latency market data, on 50+ chains or off
               Fetch real-time low-latency market data, on 50+ chains or off
@@ -50,7 +50,7 @@ type ProductLinkProps = {
   name: string;
   name: string;
   href: string;
   href: string;
   children: string;
   children: string;
-  icon: ComponentType<SVGProps<SVGSVGElement>>;
+  icon: ElementType<SVGProps<SVGSVGElement>>;
 };
 };
 
 
 const ProductLink = ({
 const ProductLink = ({
@@ -63,14 +63,14 @@ const ProductLink = ({
     as={Link}
     as={Link}
     href={href}
     href={href}
     gradient
     gradient
-    className="flex flex-col items-center gap-2 p-6 text-center sm:flex-row sm:gap-6 sm:pr-12 sm:text-left"
+    className="flex max-w-2xl flex-col items-center gap-2 p-6 text-center sm:flex-row sm:gap-6 sm:pr-12 sm:text-left"
   >
   >
     <Icon className="h-24 p-3 text-pythpurple-600 dark:text-pythpurple-400" />
     <Icon className="h-24 p-3 text-pythpurple-600 dark:text-pythpurple-400" />
     <div className="flex flex-col gap-2">
     <div className="flex flex-col gap-2">
       <h2 className="text-2xl font-medium text-pythpurple-600 dark:text-pythpurple-400">
       <h2 className="text-2xl font-medium text-pythpurple-600 dark:text-pythpurple-400">
         {name}
         {name}
       </h2>
       </h2>
-      <p className="text-sm font-normal text-neutral-600 dark:text-neutral-400">
+      <p className="text-lg font-normal text-neutral-600 dark:text-neutral-400">
         {children}
         {children}
       </p>
       </p>
     </div>
     </div>

+ 5 - 22
apps/api-reference/src/components/InlineLink/index.tsx

@@ -1,23 +1,6 @@
-import clsx from "clsx";
-import type { ComponentProps, ElementType } from "react";
+import { Styled } from "../Styled";
 
 
-type InlineLinkProps<T extends ElementType> = Omit<ComponentProps<T>, "as"> & {
-  as?: T;
-};
-
-export const InlineLink = <T extends ElementType>({
-  as,
-  className,
-  ...props
-}: InlineLinkProps<T>) => {
-  const Component = as ?? "a";
-  return (
-    <Component
-      className={clsx(
-        "font-medium text-pythpurple-600 hover:underline dark:text-pythpurple-400",
-        className,
-      )}
-      {...props}
-    />
-  );
-};
+export const InlineLink = Styled(
+  "a",
+  "font-medium text-pythpurple-600 hover:underline dark:text-pythpurple-400",
+);

+ 2 - 20
apps/api-reference/src/components/MaxWidth/index.tsx

@@ -1,21 +1,3 @@
-import clsx from "clsx";
-import type { ElementType, ComponentProps } from "react";
+import { Styled } from "../Styled";
 
 
-type MaxWidthProps<T extends ElementType> = Omit<ComponentProps<T>, "as"> & {
-  as?: T;
-};
-
-export const MaxWidth = <T extends ElementType = "div">({
-  as,
-  className,
-  ...props
-}: MaxWidthProps<T>) => {
-  const Component = as ?? "div";
-
-  return (
-    <Component
-      className={clsx("mx-auto max-w-7xl px-8", className)}
-      {...props}
-    />
-  );
-};
+export const MaxWidth = Styled("div", "mx-auto max-w-7xl px-8");

+ 2 - 8
apps/api-reference/src/components/Paragraph/index.tsx

@@ -1,9 +1,3 @@
-import clsx from "clsx";
-import type { HTMLAttributes } from "react";
+import { Styled } from "../Styled";
 
 
-export const Paragraph = ({
-  className,
-  ...props
-}: HTMLAttributes<HTMLElement>) => (
-  <p className={clsx("mb-6 last:mb-0", className)} {...props} />
-);
+export const Paragraph = Styled("p", "mb-6 last:mb-0");

+ 17 - 7
apps/api-reference/src/components/Root/index.tsx

@@ -4,9 +4,15 @@ import { Red_Hat_Text, Red_Hat_Mono } from "next/font/google";
 import { ThemeProvider } from "next-themes";
 import { ThemeProvider } from "next-themes";
 import type { ReactNode } from "react";
 import type { ReactNode } from "react";
 
 
-import { IS_PRODUCTION_BUILD } from "../../isomorphic-config";
-import { GOOGLE_ANALYTICS_ID, AMPLITUDE_API_KEY } from "../../server-config";
+import {
+  IS_PRODUCTION_SERVER,
+  GOOGLE_ANALYTICS_ID,
+  AMPLITUDE_API_KEY,
+  WALLETCONNECT_PROJECT_ID,
+} from "../../server-config";
 import { Amplitude } from "../Amplitude";
 import { Amplitude } from "../Amplitude";
+import { HighlighterProvider } from "../Code/use-highlighted-code";
+import { EvmProvider } from "../EvmProvider";
 import { Footer } from "../Footer";
 import { Footer } from "../Footer";
 import { Header } from "../Header";
 import { Header } from "../Header";
 import { ReportAccessibility } from "../ReportAccessibility";
 import { ReportAccessibility } from "../ReportAccessibility";
@@ -35,13 +41,17 @@ export const Root = ({ children }: Props) => (
   >
   >
     <body className="grid size-full grid-cols-1 grid-rows-[max-content_1fr_max-content] bg-white text-pythpurple-950 dark:bg-pythpurple-900 dark:text-white">
     <body className="grid size-full grid-cols-1 grid-rows-[max-content_1fr_max-content] bg-white text-pythpurple-950 dark:bg-pythpurple-900 dark:text-white">
       <ThemeProvider attribute="class">
       <ThemeProvider attribute="class">
-        <Header className="z-10 border-b border-neutral-400 dark:border-neutral-600" />
-        <div className="size-full">{children}</div>
-        <Footer className="z-10 border-t border-neutral-400 dark:border-neutral-600" />
+        <HighlighterProvider>
+          <EvmProvider walletConnectProjectId={WALLETCONNECT_PROJECT_ID}>
+            <Header className="z-10 border-b border-neutral-400 dark:border-neutral-600" />
+            <div className="size-full">{children}</div>
+            <Footer className="z-10 border-t border-neutral-400 dark:border-neutral-600" />
+          </EvmProvider>
+        </HighlighterProvider>
       </ThemeProvider>
       </ThemeProvider>
     </body>
     </body>
     {GOOGLE_ANALYTICS_ID && <GoogleAnalytics gaId={GOOGLE_ANALYTICS_ID} />}
     {GOOGLE_ANALYTICS_ID && <GoogleAnalytics gaId={GOOGLE_ANALYTICS_ID} />}
-    {AMPLITUDE_API_KEY && <Amplitude key={AMPLITUDE_API_KEY} />}
-    {!IS_PRODUCTION_BUILD && <ReportAccessibility />}
+    {AMPLITUDE_API_KEY && <Amplitude apiKey={AMPLITUDE_API_KEY} />}
+    {!IS_PRODUCTION_SERVER && <ReportAccessibility />}
   </html>
   </html>
 );
 );

+ 77 - 20
apps/api-reference/src/components/Sidebar/index.tsx

@@ -4,9 +4,17 @@ import { Field, Label } from "@headlessui/react";
 import clsx from "clsx";
 import clsx from "clsx";
 import Link from "next/link";
 import Link from "next/link";
 import { useSelectedLayoutSegments } from "next/navigation";
 import { useSelectedLayoutSegments } from "next/navigation";
-import { type HTMLAttributes, useState, type ComponentProps } from "react";
+import {
+  type HTMLAttributes,
+  useState,
+  type ComponentProps,
+  type ElementType,
+  Fragment,
+} from "react";
+import Markdown from "react-markdown";
 
 
 import * as apis from "../../apis";
 import * as apis from "../../apis";
+import { MARKDOWN_COMPONENTS } from "../../markdown-components";
 import { Select } from "../Select";
 import { Select } from "../Select";
 
 
 type Chain = keyof typeof apis;
 type Chain = keyof typeof apis;
@@ -22,9 +30,10 @@ const CHAIN_TO_NAME = {
 const MENU = Object.fromEntries(
 const MENU = Object.fromEntries(
   Object.entries(apis).map(([chain, methods]) => [
   Object.entries(apis).map(([chain, methods]) => [
     chain,
     chain,
-    Object.keys(methods).map((method) => ({
-      name: method,
-      href: `/price-feeds/${chain}/${method}`,
+    Object.entries(methods).map(([name, { summary }]) => ({
+      name,
+      summary,
+      href: `/price-feeds/${chain}/${name}`,
     })),
     })),
   ]),
   ]),
 );
 );
@@ -60,9 +69,11 @@ export const Sidebar = ({
           aria-label="Methods"
           aria-label="Methods"
         >
         >
           <ul className="contents">
           <ul className="contents">
-            {MENU[chain]?.map(({ name, href }) => (
+            {MENU[chain]?.map(({ name, href, summary }) => (
               <li className="contents" key={href}>
               <li className="contents" key={href}>
-                <MenuButton href={href}>{name}</MenuButton>
+                <MenuButton href={href} name={name}>
+                  {summary}
+                </MenuButton>
               </li>
               </li>
             ))}
             ))}
           </ul>
           </ul>
@@ -73,29 +84,75 @@ export const Sidebar = ({
   );
   );
 };
 };
 
 
-const baseMenuButtonClasses = "text-sm py-1 px-2";
+type MenuButtonProps = Omit<
+  ComponentProps<typeof Link>,
+  keyof MenuItemProps<typeof Link>
+> & {
+  name: string;
+  children: string;
+};
 
 
-const MenuButton = ({ className, ...props }: ComponentProps<typeof Link>) => {
+const MenuButton = ({
+  className,
+  name,
+  children,
+  ...props
+}: MenuButtonProps) => {
   const segments = useSelectedLayoutSegments();
   const segments = useSelectedLayoutSegments();
 
 
   return `/price-feeds/${segments.join("/")}` === props.href ? (
   return `/price-feeds/${segments.join("/")}` === props.href ? (
-    <div
-      className={clsx(
-        "font-bold text-pythpurple-600 dark:text-pythpurple-400",
-        baseMenuButtonClasses,
-        className,
-      )}
-    >
-      {props.children}
-    </div>
+    <MenuItem
+      className={className}
+      name={name}
+      summary={children}
+      nameClassName="font-bold text-pythpurple-600 dark:text-pythpurple-400"
+    />
   ) : (
   ) : (
-    <Link
+    <MenuItem
+      as={Link}
       className={clsx(
       className={clsx(
-        "rounded hover:bg-neutral-200 hover:text-pythpurple-600 dark:hover:bg-neutral-800 dark:hover:text-pythpurple-400",
-        baseMenuButtonClasses,
+        "group hover:bg-neutral-200 dark:hover:bg-neutral-800",
         className,
         className,
       )}
       )}
+      nameClassName="group-hover:text-pythpurple-600 dark:group-hover:text-pythpurple-400"
+      name={name}
+      summary={children}
       {...props}
       {...props}
     />
     />
   );
   );
 };
 };
+
+type MenuItemProps<T extends ElementType> = {
+  as?: T;
+  name: string;
+  summary: string;
+  nameClassName?: string | undefined;
+};
+
+const MenuItem = <T extends ElementType>({
+  as,
+  className,
+  name,
+  summary,
+  nameClassName,
+  ...props
+}: Omit<ComponentProps<T>, keyof MenuItemProps<T>> & MenuItemProps<T>) => {
+  const Component = as ?? "div";
+  return (
+    <Component className={clsx("rounded px-2 py-1", className)} {...props}>
+      <div className={clsx("text-sm", nameClassName)}>{name}</div>
+      <Markdown
+        className={clsx(
+          "ml-4 overflow-hidden text-ellipsis text-nowrap text-xs font-light",
+          className,
+        )}
+        components={{
+          ...MARKDOWN_COMPONENTS,
+          p: ({ children }) => <Fragment>{children}</Fragment>,
+        }}
+      >
+        {summary}
+      </Markdown>
+    </Component>
+  );
+};

+ 18 - 0
apps/api-reference/src/components/Styled/index.tsx

@@ -0,0 +1,18 @@
+import clsx from "clsx";
+import type { ComponentProps, ElementType } from "react";
+
+type StyledProps<T extends ElementType> = Omit<ComponentProps<T>, "as"> & {
+  as?: T;
+};
+
+export const Styled = (defaultElement: ElementType, classes: string) => {
+  const StyledComponent = <T extends ElementType = typeof defaultElement>({
+    as,
+    className,
+    ...props
+  }: StyledProps<T>) => {
+    const Component = as ?? defaultElement;
+    return <Component className={clsx(classes, className)} {...props} />;
+  };
+  return StyledComponent;
+};

+ 12 - 0
apps/api-reference/src/markdown-components.tsx

@@ -0,0 +1,12 @@
+import { InlineCode } from "./components/InlineCode";
+import { InlineLink } from "./components/InlineLink";
+import { Paragraph } from "./components/Paragraph";
+import { Styled } from "./components/Styled";
+
+export const MARKDOWN_COMPONENTS = {
+  h1: Styled("h1", "mb-8 text-4xl font-medium"),
+  p: Paragraph,
+  a: InlineLink,
+  code: InlineCode,
+  strong: Styled("strong", "font-semibold"),
+};

+ 0 - 18
apps/api-reference/src/mdx-components.tsx

@@ -1,18 +0,0 @@
-import type { MDXComponents } from "mdx/types";
-
-import { InlineCode } from "./components/InlineCode";
-import { InlineLink } from "./components/InlineLink";
-import { Paragraph } from "./components/Paragraph";
-
-export const useMDXComponents = (components: MDXComponents): MDXComponents => ({
-  h1: ({ children, ...props }) => (
-    <h1 className="mb-8 text-4xl font-medium" {...props}>
-      {children}
-    </h1>
-  ),
-  p: Paragraph,
-  a: InlineLink,
-  code: InlineCode,
-  strong: (props) => <strong className="font-semibold" {...props} />,
-  ...components,
-});

+ 42 - 1
apps/api-reference/src/server-config.ts

@@ -4,23 +4,64 @@
 
 
 import "server-only";
 import "server-only";
 
 
+/**
+ * Throw if the env var `key` is not set (at either runtime or build time).
+ */
 const demand = (key: string): string => {
 const demand = (key: string): string => {
   const value = process.env[key];
   const value = process.env[key];
-  if (value) {
+  if (value && value !== "") {
     return value;
     return value;
   } else {
   } else {
     throw new Error(`Missing environment variable ${key}!`);
     throw new Error(`Missing environment variable ${key}!`);
   }
   }
 };
 };
 
 
+/**
+ * Indicates that we're running in a github actions workflow.
+ */
+export const IS_GITHUB_ACTIONS = process.env.GITHUB_ACTIONS === "true";
+
+/**
+ * Throw if the env var `key` is not set, unless we're running in github
+ * actions.  If running in GHA, then allow the variable to be unset and return
+ * an empty string if so.
+ *
+ * This is useful because for some variables, we want an invariant that the
+ * value is always present.  However, we don't necessarily need the value just
+ * to run code checks, some of which require a build, we don't want to
+ * expose the secret to GHA unnecessarily, and we don't want to have to.
+ *
+ * So, in effect, variables marked with this will be asserted to be present in
+ * Vercel or in local dev, and will type check as `string`, but will not throw
+ * when running code checks in GHA.
+ *
+ * Note we use `IS_GITHUB_ACTIONS` and not e.g. `process.env.CI` here because
+ * both Vercel and Github Actions set `process.env.CI`, and we specifically want
+ * to only allow these variables to be nonpresent only if running a build just
+ * for running code checks.  Any build that will actually serve traffic must
+ * have these variables set.  Github Actions is the only environment that is
+ * exclusively used for running checks, so semantically this is correct, but
+ * this would need to be updated if we change infrastructure.
+ */
+const demandExceptGHA = IS_GITHUB_ACTIONS
+  ? (key: string) => process.env[key] ?? ""
+  : demand;
+
 /**
 /**
  * Indicates that this server is the live customer-facing production server.
  * Indicates that this server is the live customer-facing production server.
  */
  */
 export const IS_PRODUCTION_SERVER = process.env.VERCEL_ENV === "production";
 export const IS_PRODUCTION_SERVER = process.env.VERCEL_ENV === "production";
 
 
+/**
+ * Throw if the env var `key` is not set in the live customer-facing production
+ * server, but allow it to be unset in any other environment.
+ */
 const demandInProduction = IS_PRODUCTION_SERVER
 const demandInProduction = IS_PRODUCTION_SERVER
   ? demand
   ? demand
   : (key: string) => process.env[key];
   : (key: string) => process.env[key];
 
 
 export const GOOGLE_ANALYTICS_ID = demandInProduction("GOOGLE_ANALYTICS_ID");
 export const GOOGLE_ANALYTICS_ID = demandInProduction("GOOGLE_ANALYTICS_ID");
 export const AMPLITUDE_API_KEY = demandInProduction("AMPLITUDE_API_KEY");
 export const AMPLITUDE_API_KEY = demandInProduction("AMPLITUDE_API_KEY");
+export const WALLETCONNECT_PROJECT_ID = demandExceptGHA(
+  "WALLETCONNECT_PROJECT_ID",
+);

+ 1 - 1
apps/api-reference/tailwind.config.ts

@@ -3,7 +3,7 @@ import type { Config } from "tailwindcss";
 
 
 const tailwindConfig = {
 const tailwindConfig = {
   darkMode: "class",
   darkMode: "class",
-  content: ["src/components/**/*.{ts,tsx}", "src/mdx-components.tsx"],
+  content: ["src/components/**/*.{ts,tsx}", "src/markdown-components.tsx"],
   plugins: [forms],
   plugins: [forms],
   theme: {
   theme: {
     extend: {
     extend: {

+ 19 - 15
flake.nix

@@ -19,27 +19,31 @@
         };
         };
 
 
         cli-overlay = _: prev: {
         cli-overlay = _: prev: {
-          cli = prev.lib.mkCli "cli" {
-            _noAll = true;
+          cli = let
+            pnpm = "${prev.pnpm}/bin/pnpm i && ${prev.pnpm}/bin/pnpm";
+          in
+            prev.lib.mkCli "cli" {
+              _noAll = true;
 
 
-            install = "${prev.pnpm}/bin/pnpm i";
+              install = "${pnpm} i";
+              start = "${pnpm} lerna run start:dev";
 
 
-            test = {
-              nix = {
-                lint = "${prev.statix}/bin/statix check --ignore node_modules .";
-                dead-code = "${prev.deadnix}/bin/deadnix --exclude ./node_modules .";
-                format = "${prev.alejandra}/bin/alejandra --exclude ./node_modules --check .";
+              test = {
+                nix = {
+                  lint = "${prev.statix}/bin/statix check --ignore node_modules .";
+                  dead-code = "${prev.deadnix}/bin/deadnix --exclude ./node_modules .";
+                  format = "${prev.alejandra}/bin/alejandra --exclude ./node_modules --check .";
+                };
               };
               };
-            };
 
 
-            fix = {
-              nix = {
-                lint = "${prev.statix}/bin/statix fix --ignore node_modules .";
-                dead-code = "${prev.deadnix}/bin/deadnix --exclude ./node_modules -e .";
-                format = "${prev.alejandra}/bin/alejandra --exclude ./node_modules .";
+              fix = {
+                nix = {
+                  lint = "${prev.statix}/bin/statix fix --ignore node_modules .";
+                  dead-code = "${prev.deadnix}/bin/deadnix --exclude ./node_modules -e .";
+                  format = "${prev.alejandra}/bin/alejandra --exclude ./node_modules .";
+                };
               };
               };
             };
             };
-          };
         };
         };
 
 
         pkgs = import nixpkgs {
         pkgs = import nixpkgs {

+ 62 - 386
pnpm-lock.yaml

@@ -33,6 +33,9 @@ importers:
       '@amplitude/analytics-browser':
       '@amplitude/analytics-browser':
         specifier: ^2.9.0
         specifier: ^2.9.0
         version: 2.9.0
         version: 2.9.0
+      '@amplitude/plugin-autocapture-browser':
+        specifier: ^0.9.0
+        version: 0.9.0
       '@floating-ui/react':
       '@floating-ui/react':
         specifier: ^0.26.17
         specifier: ^0.26.17
         version: 0.26.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
         version: 0.26.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -42,18 +45,6 @@ importers:
       '@heroicons/react':
       '@heroicons/react':
         specifier: ^2.1.4
         specifier: ^2.1.4
         version: 2.1.4(react@18.3.1)
         version: 2.1.4(react@18.3.1)
-      '@mdx-js/loader':
-        specifier: ^3.0.1
-        version: 3.0.1(webpack@5.91.0)
-      '@mdx-js/mdx':
-        specifier: ^3.0.1
-        version: 3.0.1
-      '@mdx-js/react':
-        specifier: ^3.0.1
-        version: 3.0.1(@types/react@18.3.3)(react@18.3.1)
-      '@next/mdx':
-        specifier: ^14.2.4
-        version: 14.2.4(@mdx-js/loader@3.0.1(webpack@5.91.0))(@mdx-js/react@3.0.1(@types/react@18.3.3)(react@18.3.1))
       '@next/third-parties':
       '@next/third-parties':
         specifier: ^14.2.4
         specifier: ^14.2.4
         version: 14.2.4(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
         version: 14.2.4(next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
@@ -87,6 +78,9 @@ importers:
       react-dom:
       react-dom:
         specifier: ^18.3.1
         specifier: ^18.3.1
         version: 18.3.1(react@18.3.1)
         version: 18.3.1(react@18.3.1)
+      react-markdown:
+        specifier: ^9.0.1
+        version: 9.0.1(@types/react@18.3.3)(react@18.3.1)
       shiki:
       shiki:
         specifier: ^1.7.0
         specifier: ^1.7.0
         version: 1.7.0
         version: 1.7.0
@@ -124,9 +118,6 @@ importers:
       '@types/jest':
       '@types/jest':
         specifier: ^29.5.12
         specifier: ^29.5.12
         version: 29.5.12
         version: 29.5.12
-      '@types/mdx':
-        specifier: ^2.0.13
-        version: 2.0.13
       '@types/node':
       '@types/node':
         specifier: ^20.14.6
         specifier: ^20.14.6
         version: 20.14.7
         version: 20.14.7
@@ -1741,6 +1732,9 @@ packages:
   '@amplitude/analytics-types@2.6.0':
   '@amplitude/analytics-types@2.6.0':
     resolution: {integrity: sha512-7MSENvLCTGjec7K45JT+RcOuoPTCvq1MMq/HRLiQK/BMR4taX7f/uXldEc8b//o+ZZP45IBqFroR7Bl8LwJQrQ==}
     resolution: {integrity: sha512-7MSENvLCTGjec7K45JT+RcOuoPTCvq1MMq/HRLiQK/BMR4taX7f/uXldEc8b//o+ZZP45IBqFroR7Bl8LwJQrQ==}
 
 
+  '@amplitude/plugin-autocapture-browser@0.9.0':
+    resolution: {integrity: sha512-iLae95j9tkfZtHCrfFLOsMzqmjO3NRqB+Eslf1txFfEsbZ/2MlDQqtxklvxv46JD2IH4q/vDamyJ0gGRsse16w==}
+
   '@amplitude/plugin-page-view-tracking-browser@2.2.14':
   '@amplitude/plugin-page-view-tracking-browser@2.2.14':
     resolution: {integrity: sha512-3Rh8P0pK3QOd94uVeOgjc2UommeHucCvasAIdjf1QQ59wVmNua2gY3IwR8+Pyg0PJ3plmwY/La8YukeXB6A2aA==}
     resolution: {integrity: sha512-3Rh8P0pK3QOd94uVeOgjc2UommeHucCvasAIdjf1QQ59wVmNua2gY3IwR8+Pyg0PJ3plmwY/La8YukeXB6A2aA==}
 
 
@@ -4597,20 +4591,6 @@ packages:
     peerDependencies:
     peerDependencies:
       hardhat: ^2.12.6
       hardhat: ^2.12.6
 
 
-  '@mdx-js/loader@3.0.1':
-    resolution: {integrity: sha512-YbYUt7YyEOdFxhyuCWmLKf5vKhID/hJAojEUnheJk4D8iYVLFQw+BAoBWru/dHGch1omtmZOPstsmKPyBF68Tw==}
-    peerDependencies:
-      webpack: '>=5'
-
-  '@mdx-js/mdx@3.0.1':
-    resolution: {integrity: sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==}
-
-  '@mdx-js/react@3.0.1':
-    resolution: {integrity: sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==}
-    peerDependencies:
-      '@types/react': '>=16'
-      react: '>=16'
-
   '@metamask/eth-json-rpc-provider@1.0.1':
   '@metamask/eth-json-rpc-provider@1.0.1':
     resolution: {integrity: sha512-whiUMPlAOrVGmX8aKYVPvlKyG4CpQXiNNyt74vE1xb5sPvmx5oA7B/kOi/JdBvhGQq97U1/AVdXEdk2zkP8qyA==}
     resolution: {integrity: sha512-whiUMPlAOrVGmX8aKYVPvlKyG4CpQXiNNyt74vE1xb5sPvmx5oA7B/kOi/JdBvhGQq97U1/AVdXEdk2zkP8qyA==}
     engines: {node: '>=14.0.0'}
     engines: {node: '>=14.0.0'}
@@ -4791,17 +4771,6 @@ packages:
   '@next/eslint-plugin-next@14.2.3':
   '@next/eslint-plugin-next@14.2.3':
     resolution: {integrity: sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw==}
     resolution: {integrity: sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw==}
 
 
-  '@next/mdx@14.2.4':
-    resolution: {integrity: sha512-eklTNNoH08xGy9UiKcohZmoLhmHAYaYm5ndPGQqJybaeNErgYL8fmp2tk5DRD0L54DNqMz97oN+CAEHqfqIVcw==}
-    peerDependencies:
-      '@mdx-js/loader': '>=0.15.0'
-      '@mdx-js/react': '>=0.15.0'
-    peerDependenciesMeta:
-      '@mdx-js/loader':
-        optional: true
-      '@mdx-js/react':
-        optional: true
-
   '@next/swc-darwin-arm64@14.2.3':
   '@next/swc-darwin-arm64@14.2.3':
     resolution: {integrity: sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==}
     resolution: {integrity: sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==}
     engines: {node: '>= 10'}
     engines: {node: '>= 10'}
@@ -6743,9 +6712,6 @@ packages:
   '@types/accepts@1.3.5':
   '@types/accepts@1.3.5':
     resolution: {integrity: sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==}
     resolution: {integrity: sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==}
 
 
-  '@types/acorn@4.0.6':
-    resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==}
-
   '@types/adm-zip@0.5.0':
   '@types/adm-zip@0.5.0':
     resolution: {integrity: sha512-FCJBJq9ODsQZUNURo5ILAQueuA8WJhRvuihS3ke2iI25mJlfV2LK8jG2Qj2z2AWg8U0FtWWqBHVRetceLskSaw==}
     resolution: {integrity: sha512-FCJBJq9ODsQZUNURo5ILAQueuA8WJhRvuihS3ke2iI25mJlfV2LK8jG2Qj2z2AWg8U0FtWWqBHVRetceLskSaw==}
 
 
@@ -6896,9 +6862,6 @@ packages:
   '@types/mdast@4.0.4':
   '@types/mdast@4.0.4':
     resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
     resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
 
 
-  '@types/mdx@2.0.13':
-    resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==}
-
   '@types/mime@3.0.1':
   '@types/mime@3.0.1':
     resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==}
     resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==}
 
 
@@ -8151,10 +8114,6 @@ packages:
     resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
     resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
     engines: {node: '>=8'}
     engines: {node: '>=8'}
 
 
-  astring@1.8.6:
-    resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==}
-    hasBin: true
-
   async-eventemitter@0.2.4:
   async-eventemitter@0.2.4:
     resolution: {integrity: sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw==}
     resolution: {integrity: sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw==}
 
 
@@ -8992,9 +8951,6 @@ packages:
     resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==}
     resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==}
     engines: {node: '>=0.10.0'}
     engines: {node: '>=0.10.0'}
 
 
-  collapse-white-space@2.1.0:
-    resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==}
-
   collect-v8-coverage@1.0.1:
   collect-v8-coverage@1.0.1:
     resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==}
     resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==}
 
 
@@ -10470,27 +10426,12 @@ packages:
     resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
     resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
     engines: {node: '>=4.0'}
     engines: {node: '>=4.0'}
 
 
-  estree-util-attach-comments@3.0.0:
-    resolution: {integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==}
-
-  estree-util-build-jsx@3.0.1:
-    resolution: {integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==}
-
   estree-util-is-identifier-name@3.0.0:
   estree-util-is-identifier-name@3.0.0:
     resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==}
     resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==}
 
 
-  estree-util-to-js@2.0.0:
-    resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==}
-
-  estree-util-visit@2.0.0:
-    resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==}
-
   estree-walker@2.0.2:
   estree-walker@2.0.2:
     resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
     resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
 
 
-  estree-walker@3.0.3:
-    resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
-
   esutils@2.0.3:
   esutils@2.0.3:
     resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
     resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
     engines: {node: '>=0.10.0'}
     engines: {node: '>=0.10.0'}
@@ -11374,9 +11315,6 @@ packages:
     resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
     resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
     engines: {node: '>= 0.4'}
     engines: {node: '>= 0.4'}
 
 
-  hast-util-to-estree@3.1.0:
-    resolution: {integrity: sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw==}
-
   hast-util-to-jsx-runtime@2.3.0:
   hast-util-to-jsx-runtime@2.3.0:
     resolution: {integrity: sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==}
     resolution: {integrity: sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==}
 
 
@@ -11460,6 +11398,9 @@ packages:
   html-parse-stringify@3.0.1:
   html-parse-stringify@3.0.1:
     resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==}
     resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==}
 
 
+  html-url-attributes@3.0.0:
+    resolution: {integrity: sha512-/sXbVCWayk6GDVg3ctOX6nxaVj7So40FcFAnWlWGNAB1LpYKcV5Cd10APjPjW80O7zYW2MsjBV4zZ7IZO5fVow==}
+
   htmlparser2@8.0.1:
   htmlparser2@8.0.1:
     resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==}
     resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==}
 
 
@@ -11625,9 +11566,6 @@ packages:
     resolution: {integrity: sha512-YhlQPEjNFqlGdzrBfDNRLhvoSgX7iQRgSxgsNknRQ9ITXFT7UMfVMWhBTOh2Y+25lRnGrv5Xz8yZwQ3ACR6T3A==}
     resolution: {integrity: sha512-YhlQPEjNFqlGdzrBfDNRLhvoSgX7iQRgSxgsNknRQ9ITXFT7UMfVMWhBTOh2Y+25lRnGrv5Xz8yZwQ3ACR6T3A==}
     engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
     engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
 
 
-  inline-style-parser@0.1.1:
-    resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==}
-
   inline-style-parser@0.2.3:
   inline-style-parser@0.2.3:
     resolution: {integrity: sha512-qlD8YNDqyTKTyuITrDOffsl6Tdhv+UC4hcdAVuQsK4IMQ99nSgd1MIA/Q+jQYoh9r3hVUXhYh7urSRmXPkW04g==}
     resolution: {integrity: sha512-qlD8YNDqyTKTyuITrDOffsl6Tdhv+UC4hcdAVuQsK4IMQ99nSgd1MIA/Q+jQYoh9r3hVUXhYh7urSRmXPkW04g==}
 
 
@@ -11853,9 +11791,6 @@ packages:
   is-potential-custom-element-name@1.0.1:
   is-potential-custom-element-name@1.0.1:
     resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
     resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
 
 
-  is-reference@3.0.2:
-    resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==}
-
   is-regex@1.1.4:
   is-regex@1.1.4:
     resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
     resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
     engines: {node: '>= 0.4'}
     engines: {node: '>= 0.4'}
@@ -12935,10 +12870,6 @@ packages:
     resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==}
     resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==}
     engines: {node: '>=8'}
     engines: {node: '>=8'}
 
 
-  markdown-extensions@2.0.0:
-    resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==}
-    engines: {node: '>=16'}
-
   marked@4.3.0:
   marked@4.3.0:
     resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==}
     resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==}
     engines: {node: '>= 12'}
     engines: {node: '>= 12'}
@@ -12963,9 +12894,6 @@ packages:
   mdast-util-mdx-jsx@3.1.2:
   mdast-util-mdx-jsx@3.1.2:
     resolution: {integrity: sha512-eKMQDeywY2wlHc97k5eD8VC+9ASMjN8ItEZQNGwJ6E0XWKiW/Z0V5/H8pvoXUf+y+Mj0VIgeRRbujBmFn4FTyA==}
     resolution: {integrity: sha512-eKMQDeywY2wlHc97k5eD8VC+9ASMjN8ItEZQNGwJ6E0XWKiW/Z0V5/H8pvoXUf+y+Mj0VIgeRRbujBmFn4FTyA==}
 
 
-  mdast-util-mdx@3.0.0:
-    resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==}
-
   mdast-util-mdxjs-esm@2.0.1:
   mdast-util-mdxjs-esm@2.0.1:
     resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==}
     resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==}
 
 
@@ -13106,30 +13034,12 @@ packages:
   micromark-core-commonmark@2.0.1:
   micromark-core-commonmark@2.0.1:
     resolution: {integrity: sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==}
     resolution: {integrity: sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==}
 
 
-  micromark-extension-mdx-expression@3.0.0:
-    resolution: {integrity: sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ==}
-
-  micromark-extension-mdx-jsx@3.0.0:
-    resolution: {integrity: sha512-uvhhss8OGuzR4/N17L1JwvmJIpPhAd8oByMawEKx6NVdBCbesjH4t+vjEp3ZXft9DwvlKSD07fCeI44/N0Vf2w==}
-
-  micromark-extension-mdx-md@2.0.0:
-    resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==}
-
-  micromark-extension-mdxjs-esm@3.0.0:
-    resolution: {integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==}
-
-  micromark-extension-mdxjs@3.0.0:
-    resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==}
-
   micromark-factory-destination@2.0.0:
   micromark-factory-destination@2.0.0:
     resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==}
     resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==}
 
 
   micromark-factory-label@2.0.0:
   micromark-factory-label@2.0.0:
     resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==}
     resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==}
 
 
-  micromark-factory-mdx-expression@2.0.1:
-    resolution: {integrity: sha512-F0ccWIUHRLRrYp5TC9ZYXmZo+p2AM13ggbsW4T0b5CRKP8KHVRB8t4pwtBgTxtjRmwrK0Irwm7vs2JOZabHZfg==}
-
   micromark-factory-space@2.0.0:
   micromark-factory-space@2.0.0:
     resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==}
     resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==}
 
 
@@ -13160,9 +13070,6 @@ packages:
   micromark-util-encode@2.0.0:
   micromark-util-encode@2.0.0:
     resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==}
     resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==}
 
 
-  micromark-util-events-to-acorn@2.0.2:
-    resolution: {integrity: sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA==}
-
   micromark-util-html-tag-name@2.0.0:
   micromark-util-html-tag-name@2.0.0:
     resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==}
     resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==}
 
 
@@ -14272,9 +14179,6 @@ packages:
   performance-now@2.1.0:
   performance-now@2.1.0:
     resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
     resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
 
 
-  periscopic@3.1.0:
-    resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==}
-
   picocolors@1.0.0:
   picocolors@1.0.0:
     resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
     resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
 
 
@@ -14933,6 +14837,12 @@ packages:
   react-lifecycles-compat@3.0.4:
   react-lifecycles-compat@3.0.4:
     resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==}
     resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==}
 
 
+  react-markdown@9.0.1:
+    resolution: {integrity: sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==}
+    peerDependencies:
+      '@types/react': '>=18'
+      react: '>=18'
+
   react-modal@3.16.1:
   react-modal@3.16.1:
     resolution: {integrity: sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==}
     resolution: {integrity: sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==}
     engines: {node: '>=8'}
     engines: {node: '>=8'}
@@ -15176,9 +15086,6 @@ packages:
     resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==}
     resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==}
     hasBin: true
     hasBin: true
 
 
-  remark-mdx@3.0.1:
-    resolution: {integrity: sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==}
-
   remark-parse@11.0.0:
   remark-parse@11.0.0:
     resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
     resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
 
 
@@ -15989,9 +15896,6 @@ packages:
     engines: {node: '>=4'}
     engines: {node: '>=4'}
     hasBin: true
     hasBin: true
 
 
-  style-to-object@0.4.4:
-    resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==}
-
   style-to-object@1.0.6:
   style-to-object@1.0.6:
     resolution: {integrity: sha512-khxq+Qm3xEyZfKd/y9L3oIWQimxuc4STrQKtQn8aSDRHb8mFgpukgX1hdzfrMEW6JCjyJ8p89x+IUMVnCBI1PA==}
     resolution: {integrity: sha512-khxq+Qm3xEyZfKd/y9L3oIWQimxuc4STrQKtQn8aSDRHb8mFgpukgX1hdzfrMEW6JCjyJ8p89x+IUMVnCBI1PA==}
 
 
@@ -16762,9 +16666,6 @@ packages:
   unist-util-is@6.0.0:
   unist-util-is@6.0.0:
     resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
     resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
 
 
-  unist-util-position-from-estree@2.0.0:
-    resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==}
-
   unist-util-position@5.0.0:
   unist-util-position@5.0.0:
     resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
     resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
 
 
@@ -18017,6 +17918,12 @@ snapshots:
 
 
   '@amplitude/analytics-types@2.6.0': {}
   '@amplitude/analytics-types@2.6.0': {}
 
 
+  '@amplitude/plugin-autocapture-browser@0.9.0':
+    dependencies:
+      '@amplitude/analytics-client-common': 2.2.2
+      '@amplitude/analytics-types': 2.6.0
+      tslib: 2.6.3
+
   '@amplitude/plugin-page-view-tracking-browser@2.2.14':
   '@amplitude/plugin-page-view-tracking-browser@2.2.14':
     dependencies:
     dependencies:
       '@amplitude/analytics-client-common': 2.2.2
       '@amplitude/analytics-client-common': 2.2.2
@@ -18237,10 +18144,10 @@ snapshots:
       '@babel/helpers': 7.24.7
       '@babel/helpers': 7.24.7
       '@babel/parser': 7.24.7
       '@babel/parser': 7.24.7
       '@babel/template': 7.24.7
       '@babel/template': 7.24.7
-      '@babel/traverse': 7.24.7
+      '@babel/traverse': 7.24.7(supports-color@5.5.0)
       '@babel/types': 7.24.7
       '@babel/types': 7.24.7
       convert-source-map: 2.0.0
       convert-source-map: 2.0.0
-      debug: 4.3.5
+      debug: 4.3.5(supports-color@5.5.0)
       gensync: 1.0.0-beta.2
       gensync: 1.0.0-beta.2
       json5: 2.2.3
       json5: 2.2.3
       semver: 6.3.1
       semver: 6.3.1
@@ -18459,13 +18366,6 @@ snapshots:
     dependencies:
     dependencies:
       '@babel/types': 7.24.0
       '@babel/types': 7.24.0
 
 
-  '@babel/helper-module-imports@7.24.7':
-    dependencies:
-      '@babel/traverse': 7.24.7
-      '@babel/types': 7.24.7
-    transitivePeerDependencies:
-      - supports-color
-
   '@babel/helper-module-imports@7.24.7(supports-color@5.5.0)':
   '@babel/helper-module-imports@7.24.7(supports-color@5.5.0)':
     dependencies:
     dependencies:
       '@babel/traverse': 7.24.7(supports-color@5.5.0)
       '@babel/traverse': 7.24.7(supports-color@5.5.0)
@@ -18499,7 +18399,7 @@ snapshots:
     dependencies:
     dependencies:
       '@babel/core': 7.24.7
       '@babel/core': 7.24.7
       '@babel/helper-environment-visitor': 7.24.7
       '@babel/helper-environment-visitor': 7.24.7
-      '@babel/helper-module-imports': 7.24.7
+      '@babel/helper-module-imports': 7.24.7(supports-color@5.5.0)
       '@babel/helper-simple-access': 7.24.7
       '@babel/helper-simple-access': 7.24.7
       '@babel/helper-split-export-declaration': 7.24.7
       '@babel/helper-split-export-declaration': 7.24.7
       '@babel/helper-validator-identifier': 7.24.7
       '@babel/helper-validator-identifier': 7.24.7
@@ -18590,7 +18490,7 @@ snapshots:
 
 
   '@babel/helper-simple-access@7.24.7':
   '@babel/helper-simple-access@7.24.7':
     dependencies:
     dependencies:
-      '@babel/traverse': 7.24.7
+      '@babel/traverse': 7.24.7(supports-color@5.5.0)
       '@babel/types': 7.24.7
       '@babel/types': 7.24.7
     transitivePeerDependencies:
     transitivePeerDependencies:
       - supports-color
       - supports-color
@@ -20571,21 +20471,6 @@ snapshots:
     transitivePeerDependencies:
     transitivePeerDependencies:
       - supports-color
       - supports-color
 
 
-  '@babel/traverse@7.24.7':
-    dependencies:
-      '@babel/code-frame': 7.24.7
-      '@babel/generator': 7.24.7
-      '@babel/helper-environment-visitor': 7.24.7
-      '@babel/helper-function-name': 7.24.7
-      '@babel/helper-hoist-variables': 7.24.7
-      '@babel/helper-split-export-declaration': 7.24.7
-      '@babel/parser': 7.24.7
-      '@babel/types': 7.24.7
-      debug: 4.3.5
-      globals: 11.12.0
-    transitivePeerDependencies:
-      - supports-color
-
   '@babel/traverse@7.24.7(supports-color@5.5.0)':
   '@babel/traverse@7.24.7(supports-color@5.5.0)':
     dependencies:
     dependencies:
       '@babel/code-frame': 7.24.7
       '@babel/code-frame': 7.24.7
@@ -21663,7 +21548,7 @@ snapshots:
   '@eslint/eslintrc@2.1.4':
   '@eslint/eslintrc@2.1.4':
     dependencies:
     dependencies:
       ajv: 6.12.6
       ajv: 6.12.6
-      debug: 4.3.5
+      debug: 4.3.5(supports-color@5.5.0)
       espree: 9.6.1
       espree: 9.6.1
       globals: 13.20.0
       globals: 13.20.0
       ignore: 5.3.1
       ignore: 5.3.1
@@ -22272,7 +22157,7 @@ snapshots:
       '@graphql-tools/utils': 8.9.0(graphql@15.8.0)
       '@graphql-tools/utils': 8.9.0(graphql@15.8.0)
       dataloader: 2.1.0
       dataloader: 2.1.0
       graphql: 15.8.0
       graphql: 15.8.0
-      tslib: 2.4.1
+      tslib: 2.6.3
       value-or-promise: 1.0.11
       value-or-promise: 1.0.11
     optional: true
     optional: true
 
 
@@ -22388,7 +22273,7 @@ snapshots:
   '@humanwhocodes/config-array@0.11.14':
   '@humanwhocodes/config-array@0.11.14':
     dependencies:
     dependencies:
       '@humanwhocodes/object-schema': 2.0.3
       '@humanwhocodes/object-schema': 2.0.3
-      debug: 4.3.5
+      debug: 4.3.5(supports-color@5.5.0)
       minimatch: 3.1.2
       minimatch: 3.1.2
     transitivePeerDependencies:
     transitivePeerDependencies:
       - supports-color
       - supports-color
@@ -24325,48 +24210,6 @@ snapshots:
       - encoding
       - encoding
       - supports-color
       - supports-color
 
 
-  '@mdx-js/loader@3.0.1(webpack@5.91.0)':
-    dependencies:
-      '@mdx-js/mdx': 3.0.1
-      source-map: 0.7.4
-      webpack: 5.91.0
-    transitivePeerDependencies:
-      - supports-color
-
-  '@mdx-js/mdx@3.0.1':
-    dependencies:
-      '@types/estree': 1.0.5
-      '@types/estree-jsx': 1.0.5
-      '@types/hast': 3.0.4
-      '@types/mdx': 2.0.13
-      collapse-white-space: 2.1.0
-      devlop: 1.1.0
-      estree-util-build-jsx: 3.0.1
-      estree-util-is-identifier-name: 3.0.0
-      estree-util-to-js: 2.0.0
-      estree-walker: 3.0.3
-      hast-util-to-estree: 3.1.0
-      hast-util-to-jsx-runtime: 2.3.0
-      markdown-extensions: 2.0.0
-      periscopic: 3.1.0
-      remark-mdx: 3.0.1
-      remark-parse: 11.0.0
-      remark-rehype: 11.1.0
-      source-map: 0.7.4
-      unified: 11.0.5
-      unist-util-position-from-estree: 2.0.0
-      unist-util-stringify-position: 4.0.0
-      unist-util-visit: 5.0.0
-      vfile: 6.0.1
-    transitivePeerDependencies:
-      - supports-color
-
-  '@mdx-js/react@3.0.1(@types/react@18.3.3)(react@18.3.1)':
-    dependencies:
-      '@types/mdx': 2.0.13
-      '@types/react': 18.3.3
-      react: 18.3.1
-
   '@metamask/eth-json-rpc-provider@1.0.1':
   '@metamask/eth-json-rpc-provider@1.0.1':
     dependencies:
     dependencies:
       '@metamask/json-rpc-engine': 7.3.3
       '@metamask/json-rpc-engine': 7.3.3
@@ -24766,13 +24609,6 @@ snapshots:
     dependencies:
     dependencies:
       glob: 10.3.10
       glob: 10.3.10
 
 
-  '@next/mdx@14.2.4(@mdx-js/loader@3.0.1(webpack@5.91.0))(@mdx-js/react@3.0.1(@types/react@18.3.3)(react@18.3.1))':
-    dependencies:
-      source-map: 0.7.4
-    optionalDependencies:
-      '@mdx-js/loader': 3.0.1(webpack@5.91.0)
-      '@mdx-js/react': 3.0.1(@types/react@18.3.3)(react@18.3.1)
-
   '@next/swc-darwin-arm64@14.2.3':
   '@next/swc-darwin-arm64@14.2.3':
     optional: true
     optional: true
 
 
@@ -27998,10 +27834,6 @@ snapshots:
       '@types/node': 20.14.9
       '@types/node': 20.14.9
     optional: true
     optional: true
 
 
-  '@types/acorn@4.0.6':
-    dependencies:
-      '@types/estree': 1.0.5
-
   '@types/adm-zip@0.5.0':
   '@types/adm-zip@0.5.0':
     dependencies:
     dependencies:
       '@types/node': 20.14.9
       '@types/node': 20.14.9
@@ -28203,8 +28035,6 @@ snapshots:
     dependencies:
     dependencies:
       '@types/unist': 3.0.2
       '@types/unist': 3.0.2
 
 
-  '@types/mdx@2.0.13': {}
-
   '@types/mime@3.0.1': {}
   '@types/mime@3.0.1': {}
 
 
   '@types/minimatch@3.0.5': {}
   '@types/minimatch@3.0.5': {}
@@ -28432,7 +28262,7 @@ snapshots:
       '@typescript-eslint/type-utils': 6.21.0(eslint@8.57.0)(typescript@5.5.2)
       '@typescript-eslint/type-utils': 6.21.0(eslint@8.57.0)(typescript@5.5.2)
       '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.5.2)
       '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.5.2)
       '@typescript-eslint/visitor-keys': 6.21.0
       '@typescript-eslint/visitor-keys': 6.21.0
-      debug: 4.3.4
+      debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
       eslint: 8.57.0
       graphemer: 1.4.0
       graphemer: 1.4.0
       ignore: 5.3.1
       ignore: 5.3.1
@@ -28545,7 +28375,7 @@ snapshots:
       '@typescript-eslint/types': 6.21.0
       '@typescript-eslint/types': 6.21.0
       '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.5.2)
       '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.5.2)
       '@typescript-eslint/visitor-keys': 6.21.0
       '@typescript-eslint/visitor-keys': 6.21.0
-      debug: 4.3.4
+      debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
       eslint: 8.57.0
     optionalDependencies:
     optionalDependencies:
       typescript: 5.5.2
       typescript: 5.5.2
@@ -28661,7 +28491,7 @@ snapshots:
     dependencies:
     dependencies:
       '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.5.2)
       '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.5.2)
       '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.5.2)
       '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.5.2)
-      debug: 4.3.4
+      debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
       eslint: 8.57.0
       ts-api-utils: 1.3.0(typescript@5.5.2)
       ts-api-utils: 1.3.0(typescript@5.5.2)
     optionalDependencies:
     optionalDependencies:
@@ -28752,7 +28582,7 @@ snapshots:
     dependencies:
     dependencies:
       '@typescript-eslint/types': 6.21.0
       '@typescript-eslint/types': 6.21.0
       '@typescript-eslint/visitor-keys': 6.21.0
       '@typescript-eslint/visitor-keys': 6.21.0
-      debug: 4.3.4
+      debug: 4.3.4(supports-color@8.1.1)
       globby: 11.1.0
       globby: 11.1.0
       is-glob: 4.0.3
       is-glob: 4.0.3
       minimatch: 9.0.3
       minimatch: 9.0.3
@@ -30011,7 +29841,7 @@ snapshots:
 
 
   agent-base@6.0.2:
   agent-base@6.0.2:
     dependencies:
     dependencies:
-      debug: 4.3.5
+      debug: 4.3.5(supports-color@5.5.0)
     transitivePeerDependencies:
     transitivePeerDependencies:
       - supports-color
       - supports-color
 
 
@@ -30427,8 +30257,6 @@ snapshots:
 
 
   astral-regex@2.0.0: {}
   astral-regex@2.0.0: {}
 
 
-  astring@1.8.6: {}
-
   async-eventemitter@0.2.4:
   async-eventemitter@0.2.4:
     dependencies:
     dependencies:
       async: 2.6.4
       async: 2.6.4
@@ -30567,7 +30395,7 @@ snapshots:
 
 
   axios@1.7.2:
   axios@1.7.2:
     dependencies:
     dependencies:
-      follow-redirects: 1.15.6
+      follow-redirects: 1.15.6(debug@4.3.4)
       form-data: 4.0.0
       form-data: 4.0.0
       proxy-from-env: 1.1.0
       proxy-from-env: 1.1.0
     transitivePeerDependencies:
     transitivePeerDependencies:
@@ -31528,8 +31356,6 @@ snapshots:
 
 
   code-point-at@1.1.0: {}
   code-point-at@1.1.0: {}
 
 
-  collapse-white-space@2.1.0: {}
-
   collect-v8-coverage@1.0.1: {}
   collect-v8-coverage@1.0.1: {}
 
 
   collect-v8-coverage@1.0.2: {}
   collect-v8-coverage@1.0.2: {}
@@ -32272,20 +32098,12 @@ snapshots:
     optionalDependencies:
     optionalDependencies:
       supports-color: 8.1.1
       supports-color: 8.1.1
 
 
-  debug@4.3.4:
-    dependencies:
-      ms: 2.1.2
-
   debug@4.3.4(supports-color@8.1.1):
   debug@4.3.4(supports-color@8.1.1):
     dependencies:
     dependencies:
       ms: 2.1.2
       ms: 2.1.2
     optionalDependencies:
     optionalDependencies:
       supports-color: 8.1.1
       supports-color: 8.1.1
 
 
-  debug@4.3.5:
-    dependencies:
-      ms: 2.1.2
-
   debug@4.3.5(supports-color@5.5.0):
   debug@4.3.5(supports-color@5.5.0):
     dependencies:
     dependencies:
       ms: 2.1.2
       ms: 2.1.2
@@ -33440,7 +33258,7 @@ snapshots:
       ajv: 6.12.6
       ajv: 6.12.6
       chalk: 4.1.2
       chalk: 4.1.2
       cross-spawn: 7.0.3
       cross-spawn: 7.0.3
-      debug: 4.3.4
+      debug: 4.3.4(supports-color@8.1.1)
       doctrine: 3.0.0
       doctrine: 3.0.0
       escape-string-regexp: 4.0.0
       escape-string-regexp: 4.0.0
       eslint-scope: 7.2.2
       eslint-scope: 7.2.2
@@ -33535,36 +33353,10 @@ snapshots:
 
 
   estraverse@5.3.0: {}
   estraverse@5.3.0: {}
 
 
-  estree-util-attach-comments@3.0.0:
-    dependencies:
-      '@types/estree': 1.0.5
-
-  estree-util-build-jsx@3.0.1:
-    dependencies:
-      '@types/estree-jsx': 1.0.5
-      devlop: 1.1.0
-      estree-util-is-identifier-name: 3.0.0
-      estree-walker: 3.0.3
-
   estree-util-is-identifier-name@3.0.0: {}
   estree-util-is-identifier-name@3.0.0: {}
 
 
-  estree-util-to-js@2.0.0:
-    dependencies:
-      '@types/estree-jsx': 1.0.5
-      astring: 1.8.6
-      source-map: 0.7.4
-
-  estree-util-visit@2.0.0:
-    dependencies:
-      '@types/estree-jsx': 1.0.5
-      '@types/unist': 3.0.2
-
   estree-walker@2.0.2: {}
   estree-walker@2.0.2: {}
 
 
-  estree-walker@3.0.3:
-    dependencies:
-      '@types/estree': 1.0.5
-
   esutils@2.0.3: {}
   esutils@2.0.3: {}
 
 
   etag@1.8.1: {}
   etag@1.8.1: {}
@@ -34317,8 +34109,6 @@ snapshots:
 
 
   flow-parser@0.238.2: {}
   flow-parser@0.238.2: {}
 
 
-  follow-redirects@1.15.6: {}
-
   follow-redirects@1.15.6(debug@4.3.4):
   follow-redirects@1.15.6(debug@4.3.4):
     optionalDependencies:
     optionalDependencies:
       debug: 4.3.4(supports-color@8.1.1)
       debug: 4.3.4(supports-color@8.1.1)
@@ -34990,27 +34780,6 @@ snapshots:
     dependencies:
     dependencies:
       function-bind: 1.1.2
       function-bind: 1.1.2
 
 
-  hast-util-to-estree@3.1.0:
-    dependencies:
-      '@types/estree': 1.0.5
-      '@types/estree-jsx': 1.0.5
-      '@types/hast': 3.0.4
-      comma-separated-tokens: 2.0.3
-      devlop: 1.1.0
-      estree-util-attach-comments: 3.0.0
-      estree-util-is-identifier-name: 3.0.0
-      hast-util-whitespace: 3.0.0
-      mdast-util-mdx-expression: 2.0.0
-      mdast-util-mdx-jsx: 3.1.2
-      mdast-util-mdxjs-esm: 2.0.1
-      property-information: 6.5.0
-      space-separated-tokens: 2.0.2
-      style-to-object: 0.4.4
-      unist-util-position: 5.0.0
-      zwitch: 2.0.4
-    transitivePeerDependencies:
-      - supports-color
-
   hast-util-to-jsx-runtime@2.3.0:
   hast-util-to-jsx-runtime@2.3.0:
     dependencies:
     dependencies:
       '@types/estree': 1.0.5
       '@types/estree': 1.0.5
@@ -35108,6 +34877,8 @@ snapshots:
     dependencies:
     dependencies:
       void-elements: 3.1.0
       void-elements: 3.1.0
 
 
+  html-url-attributes@3.0.0: {}
+
   htmlparser2@8.0.1:
   htmlparser2@8.0.1:
     dependencies:
     dependencies:
       domelementtype: 2.3.0
       domelementtype: 2.3.0
@@ -35160,7 +34931,7 @@ snapshots:
     dependencies:
     dependencies:
       '@tootallnate/once': 1.1.2
       '@tootallnate/once': 1.1.2
       agent-base: 6.0.2
       agent-base: 6.0.2
-      debug: 4.3.5
+      debug: 4.3.5(supports-color@5.5.0)
     transitivePeerDependencies:
     transitivePeerDependencies:
       - supports-color
       - supports-color
 
 
@@ -35195,7 +34966,7 @@ snapshots:
   https-proxy-agent@5.0.1:
   https-proxy-agent@5.0.1:
     dependencies:
     dependencies:
       agent-base: 6.0.2
       agent-base: 6.0.2
-      debug: 4.3.5
+      debug: 4.3.5(supports-color@5.5.0)
     transitivePeerDependencies:
     transitivePeerDependencies:
       - supports-color
       - supports-color
 
 
@@ -35293,8 +35064,6 @@ snapshots:
       validate-npm-package-license: 3.0.4
       validate-npm-package-license: 3.0.4
       validate-npm-package-name: 4.0.0
       validate-npm-package-name: 4.0.0
 
 
-  inline-style-parser@0.1.1: {}
-
   inline-style-parser@0.2.3: {}
   inline-style-parser@0.2.3: {}
 
 
   inquirer@8.2.5:
   inquirer@8.2.5:
@@ -35486,10 +35255,6 @@ snapshots:
 
 
   is-potential-custom-element-name@1.0.1: {}
   is-potential-custom-element-name@1.0.1: {}
 
 
-  is-reference@3.0.2:
-    dependencies:
-      '@types/estree': 1.0.5
-
   is-regex@1.1.4:
   is-regex@1.1.4:
     dependencies:
     dependencies:
       call-bind: 1.0.7
       call-bind: 1.0.7
@@ -35658,7 +35423,7 @@ snapshots:
 
 
   istanbul-lib-source-maps@4.0.1:
   istanbul-lib-source-maps@4.0.1:
     dependencies:
     dependencies:
-      debug: 4.3.5
+      debug: 4.3.5(supports-color@5.5.0)
       istanbul-lib-coverage: 3.2.2
       istanbul-lib-coverage: 3.2.2
       source-map: 0.6.1
       source-map: 0.6.1
     transitivePeerDependencies:
     transitivePeerDependencies:
@@ -36923,7 +36688,7 @@ snapshots:
       '@babel/core': 7.24.7
       '@babel/core': 7.24.7
       '@babel/generator': 7.24.7
       '@babel/generator': 7.24.7
       '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.24.7)
       '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.24.7)
-      '@babel/traverse': 7.24.7
+      '@babel/traverse': 7.24.7(supports-color@5.5.0)
       '@babel/types': 7.24.7
       '@babel/types': 7.24.7
       '@jest/transform': 27.5.1
       '@jest/transform': 27.5.1
       '@jest/types': 27.5.1
       '@jest/types': 27.5.1
@@ -37977,8 +37742,6 @@ snapshots:
 
 
   map-obj@4.3.0: {}
   map-obj@4.3.0: {}
 
 
-  markdown-extensions@2.0.0: {}
-
   marked@4.3.0: {}
   marked@4.3.0: {}
 
 
   marky@1.2.5: {}
   marky@1.2.5: {}
@@ -38037,16 +37800,6 @@ snapshots:
     transitivePeerDependencies:
     transitivePeerDependencies:
       - supports-color
       - supports-color
 
 
-  mdast-util-mdx@3.0.0:
-    dependencies:
-      mdast-util-from-markdown: 2.0.1
-      mdast-util-mdx-expression: 2.0.0
-      mdast-util-mdx-jsx: 3.1.2
-      mdast-util-mdxjs-esm: 2.0.1
-      mdast-util-to-markdown: 2.1.0
-    transitivePeerDependencies:
-      - supports-color
-
   mdast-util-mdxjs-esm@2.0.1:
   mdast-util-mdxjs-esm@2.0.1:
     dependencies:
     dependencies:
       '@types/estree-jsx': 1.0.5
       '@types/estree-jsx': 1.0.5
@@ -38353,57 +38106,6 @@ snapshots:
       micromark-util-symbol: 2.0.0
       micromark-util-symbol: 2.0.0
       micromark-util-types: 2.0.0
       micromark-util-types: 2.0.0
 
 
-  micromark-extension-mdx-expression@3.0.0:
-    dependencies:
-      '@types/estree': 1.0.5
-      devlop: 1.1.0
-      micromark-factory-mdx-expression: 2.0.1
-      micromark-factory-space: 2.0.0
-      micromark-util-character: 2.1.0
-      micromark-util-events-to-acorn: 2.0.2
-      micromark-util-symbol: 2.0.0
-      micromark-util-types: 2.0.0
-
-  micromark-extension-mdx-jsx@3.0.0:
-    dependencies:
-      '@types/acorn': 4.0.6
-      '@types/estree': 1.0.5
-      devlop: 1.1.0
-      estree-util-is-identifier-name: 3.0.0
-      micromark-factory-mdx-expression: 2.0.1
-      micromark-factory-space: 2.0.0
-      micromark-util-character: 2.1.0
-      micromark-util-symbol: 2.0.0
-      micromark-util-types: 2.0.0
-      vfile-message: 4.0.2
-
-  micromark-extension-mdx-md@2.0.0:
-    dependencies:
-      micromark-util-types: 2.0.0
-
-  micromark-extension-mdxjs-esm@3.0.0:
-    dependencies:
-      '@types/estree': 1.0.5
-      devlop: 1.1.0
-      micromark-core-commonmark: 2.0.1
-      micromark-util-character: 2.1.0
-      micromark-util-events-to-acorn: 2.0.2
-      micromark-util-symbol: 2.0.0
-      micromark-util-types: 2.0.0
-      unist-util-position-from-estree: 2.0.0
-      vfile-message: 4.0.2
-
-  micromark-extension-mdxjs@3.0.0:
-    dependencies:
-      acorn: 8.11.3
-      acorn-jsx: 5.3.2(acorn@8.11.3)
-      micromark-extension-mdx-expression: 3.0.0
-      micromark-extension-mdx-jsx: 3.0.0
-      micromark-extension-mdx-md: 2.0.0
-      micromark-extension-mdxjs-esm: 3.0.0
-      micromark-util-combine-extensions: 2.0.0
-      micromark-util-types: 2.0.0
-
   micromark-factory-destination@2.0.0:
   micromark-factory-destination@2.0.0:
     dependencies:
     dependencies:
       micromark-util-character: 2.1.0
       micromark-util-character: 2.1.0
@@ -38417,17 +38119,6 @@ snapshots:
       micromark-util-symbol: 2.0.0
       micromark-util-symbol: 2.0.0
       micromark-util-types: 2.0.0
       micromark-util-types: 2.0.0
 
 
-  micromark-factory-mdx-expression@2.0.1:
-    dependencies:
-      '@types/estree': 1.0.5
-      devlop: 1.1.0
-      micromark-util-character: 2.1.0
-      micromark-util-events-to-acorn: 2.0.2
-      micromark-util-symbol: 2.0.0
-      micromark-util-types: 2.0.0
-      unist-util-position-from-estree: 2.0.0
-      vfile-message: 4.0.2
-
   micromark-factory-space@2.0.0:
   micromark-factory-space@2.0.0:
     dependencies:
     dependencies:
       micromark-util-character: 2.1.0
       micromark-util-character: 2.1.0
@@ -38480,17 +38171,6 @@ snapshots:
 
 
   micromark-util-encode@2.0.0: {}
   micromark-util-encode@2.0.0: {}
 
 
-  micromark-util-events-to-acorn@2.0.2:
-    dependencies:
-      '@types/acorn': 4.0.6
-      '@types/estree': 1.0.5
-      '@types/unist': 3.0.2
-      devlop: 1.1.0
-      estree-util-visit: 2.0.0
-      micromark-util-symbol: 2.0.0
-      micromark-util-types: 2.0.0
-      vfile-message: 4.0.2
-
   micromark-util-html-tag-name@2.0.0: {}
   micromark-util-html-tag-name@2.0.0: {}
 
 
   micromark-util-normalize-identifier@2.0.0:
   micromark-util-normalize-identifier@2.0.0:
@@ -39817,12 +39497,6 @@ snapshots:
 
 
   performance-now@2.1.0: {}
   performance-now@2.1.0: {}
 
 
-  periscopic@3.1.0:
-    dependencies:
-      '@types/estree': 1.0.5
-      estree-walker: 3.0.3
-      is-reference: 3.0.2
-
   picocolors@1.0.0: {}
   picocolors@1.0.0: {}
 
 
   picocolors@1.0.1: {}
   picocolors@1.0.1: {}
@@ -40648,6 +40322,23 @@ snapshots:
 
 
   react-lifecycles-compat@3.0.4: {}
   react-lifecycles-compat@3.0.4: {}
 
 
+  react-markdown@9.0.1(@types/react@18.3.3)(react@18.3.1):
+    dependencies:
+      '@types/hast': 3.0.4
+      '@types/react': 18.3.3
+      devlop: 1.1.0
+      hast-util-to-jsx-runtime: 2.3.0
+      html-url-attributes: 3.0.0
+      mdast-util-to-hast: 13.2.0
+      react: 18.3.1
+      remark-parse: 11.0.0
+      remark-rehype: 11.1.0
+      unified: 11.0.5
+      unist-util-visit: 5.0.0
+      vfile: 6.0.1
+    transitivePeerDependencies:
+      - supports-color
+
   react-modal@3.16.1(react-dom@16.13.1(react@16.13.1))(react@16.13.1):
   react-modal@3.16.1(react-dom@16.13.1(react@16.13.1))(react@16.13.1):
     dependencies:
     dependencies:
       exenv: 1.2.2
       exenv: 1.2.2
@@ -41044,13 +40735,6 @@ snapshots:
     dependencies:
     dependencies:
       jsesc: 0.5.0
       jsesc: 0.5.0
 
 
-  remark-mdx@3.0.1:
-    dependencies:
-      mdast-util-mdx: 3.0.0
-      micromark-extension-mdxjs: 3.0.0
-    transitivePeerDependencies:
-      - supports-color
-
   remark-parse@11.0.0:
   remark-parse@11.0.0:
     dependencies:
     dependencies:
       '@types/mdast': 4.0.4
       '@types/mdast': 4.0.4
@@ -42007,10 +41691,6 @@ snapshots:
       minimist: 1.2.7
       minimist: 1.2.7
       through: 2.3.8
       through: 2.3.8
 
 
-  style-to-object@0.4.4:
-    dependencies:
-      inline-style-parser: 0.1.1
-
   style-to-object@1.0.6:
   style-to-object@1.0.6:
     dependencies:
     dependencies:
       inline-style-parser: 0.2.3
       inline-style-parser: 0.2.3
@@ -42949,7 +42629,7 @@ snapshots:
       '@tsconfig/node14': 1.0.3
       '@tsconfig/node14': 1.0.3
       '@tsconfig/node16': 1.0.3
       '@tsconfig/node16': 1.0.3
       '@types/node': 20.14.9
       '@types/node': 20.14.9
-      acorn: 8.12.0
+      acorn: 8.11.3
       acorn-walk: 8.2.0
       acorn-walk: 8.2.0
       arg: 4.1.3
       arg: 4.1.3
       create-require: 1.1.1
       create-require: 1.1.1
@@ -43236,10 +42916,6 @@ snapshots:
     dependencies:
     dependencies:
       '@types/unist': 3.0.2
       '@types/unist': 3.0.2
 
 
-  unist-util-position-from-estree@2.0.0:
-    dependencies:
-      '@types/unist': 3.0.2
-
   unist-util-position@5.0.0:
   unist-util-position@5.0.0:
     dependencies:
     dependencies:
       '@types/unist': 3.0.2
       '@types/unist': 3.0.2