Sfoglia il codice sorgente

Merge pull request #3160 from pyth-network/bduran/dual-publish

Bduran/dual publish
Ben Duran 2 settimane fa
parent
commit
a52f61e9eb
100 ha cambiato i file con 1038 aggiunte e 2876 eliminazioni
  1. 2 1
      .gitignore
  2. 2 0
      apps/api-reference/.gitignore
  3. 6 0
      apps/api-reference/.prettierignore
  4. 3 1
      apps/api-reference/jest.config.js
  5. 3 4
      apps/api-reference/package.json
  6. 2 0
      apps/argus/.gitignore
  7. 2 1
      apps/developer-hub/.gitignore
  8. 6 0
      apps/developer-hub/.prettierignore
  9. 3 1
      apps/developer-hub/jest.config.js
  10. 3 4
      apps/developer-hub/package.json
  11. 0 1
      apps/entropy-debugger/.gitignore
  12. 0 7
      apps/entropy-debugger/.prettierignore
  13. 0 0
      apps/entropy-debugger/README.md
  14. 0 14
      apps/entropy-debugger/components.json
  15. 0 1
      apps/entropy-debugger/eslint.config.js
  16. 0 1
      apps/entropy-debugger/jest.config.js
  17. 0 6
      apps/entropy-debugger/next-env.d.ts
  18. 0 49
      apps/entropy-debugger/next.config.js
  19. 0 52
      apps/entropy-debugger/package.json
  20. 0 8
      apps/entropy-debugger/postcss.config.js
  21. 0 1
      apps/entropy-debugger/prettier.config.js
  22. 0 7
      apps/entropy-debugger/prettierignore
  23. BIN
      apps/entropy-debugger/src/app/favicon.ico
  24. BIN
      apps/entropy-debugger/src/app/fonts/GeistMonoVF.woff
  25. BIN
      apps/entropy-debugger/src/app/fonts/GeistVF.woff
  26. 0 72
      apps/entropy-debugger/src/app/globals.css
  27. 0 35
      apps/entropy-debugger/src/app/layout.tsx
  28. 0 240
      apps/entropy-debugger/src/app/page.tsx
  29. 0 56
      apps/entropy-debugger/src/components/ui/button.tsx
  30. 0 13
      apps/entropy-debugger/src/components/ui/input.tsx
  31. 0 146
      apps/entropy-debugger/src/components/ui/select.tsx
  32. 0 25
      apps/entropy-debugger/src/components/ui/switch.tsx
  33. 0 1000
      apps/entropy-debugger/src/lib/entropy-abi.ts
  34. 0 83
      apps/entropy-debugger/src/lib/revelation.ts
  35. 0 5
      apps/entropy-debugger/src/lib/utils.ts
  36. 0 350
      apps/entropy-debugger/src/store/entropy-deployments.ts
  37. 0 63
      apps/entropy-debugger/tailwind.config.ts
  38. 0 5
      apps/entropy-debugger/tsconfig.json
  39. 0 3
      apps/entropy-debugger/vercel.json
  40. 2 0
      apps/entropy-explorer/.gitignore
  41. 6 0
      apps/entropy-explorer/.prettierignore
  42. 4 1
      apps/entropy-explorer/jest.config.js
  43. 3 4
      apps/entropy-explorer/package.json
  44. 9 0
      apps/entropy-explorer/tsconfig.build.json
  45. 2 0
      apps/entropy-tester/.gitignore
  46. 6 0
      apps/entropy-tester/.prettierignore
  47. 16 12
      apps/entropy-tester/package.json
  48. 1 1
      apps/entropy-tester/src/index.ts
  49. 12 3
      apps/entropy-tester/tsconfig.build.json
  50. 2 0
      apps/fortuna/.gitignore
  51. 0 6
      apps/hermes/client/js/.eslintrc.js
  52. 2 0
      apps/hermes/client/js/.gitignore
  53. 7 0
      apps/hermes/client/js/.prettierignore
  54. 12 0
      apps/hermes/client/js/eslint.config.js
  55. 3 5
      apps/hermes/client/js/jest.config.js
  56. 60 13
      apps/hermes/client/js/package.json
  57. 1 3
      apps/hermes/client/js/src/examples/hermes-client.ts
  58. 34 23
      apps/hermes/client/js/src/hermes-client.ts
  59. 1 0
      apps/hermes/client/js/src/index.ts
  60. 7 4
      apps/hermes/client/js/src/utils.ts
  61. 15 0
      apps/hermes/client/js/tsconfig.build.json
  62. 3 12
      apps/hermes/client/js/tsconfig.json
  63. 2 0
      apps/hermes/server/.gitignore
  64. 2 0
      apps/insights/.gitignore
  65. 6 0
      apps/insights/.prettierignore
  66. 4 1
      apps/insights/jest.config.js
  67. 3 4
      apps/insights/package.json
  68. 9 0
      apps/insights/tsconfig.build.json
  69. 0 10
      apps/price_pusher/.eslintrc.js
  70. 2 0
      apps/price_pusher/.gitignore
  71. 6 0
      apps/price_pusher/.prettierignore
  72. 12 0
      apps/price_pusher/eslint.config.js
  73. 3 5
      apps/price_pusher/jest.config.js
  74. 155 14
      apps/price_pusher/package.json
  75. 37 35
      apps/price_pusher/src/aptos/aptos.ts
  76. 12 11
      apps/price_pusher/src/aptos/balance-tracker.ts
  77. 22 15
      apps/price_pusher/src/aptos/command.ts
  78. 11 8
      apps/price_pusher/src/controller.ts
  79. 12 11
      apps/price_pusher/src/evm/balance-tracker.ts
  80. 23 16
      apps/price_pusher/src/evm/command.ts
  81. 15 12
      apps/price_pusher/src/evm/custom-gas-station.ts
  82. 64 67
      apps/price_pusher/src/evm/evm.ts
  83. 5 3
      apps/price_pusher/src/evm/pyth-contract.ts
  84. 12 8
      apps/price_pusher/src/evm/super-wallet.ts
  85. 18 9
      apps/price_pusher/src/fuel/command.ts
  86. 36 31
      apps/price_pusher/src/fuel/fuel.ts
  87. 15 12
      apps/price_pusher/src/index.ts
  88. 17 12
      apps/price_pusher/src/injective/command.ts
  89. 41 38
      apps/price_pusher/src/injective/injective.ts
  90. 17 16
      apps/price_pusher/src/interface.ts
  91. 17 14
      apps/price_pusher/src/metrics.ts
  92. 8 4
      apps/price_pusher/src/near/command.ts
  93. 44 39
      apps/price_pusher/src/near/near.ts
  94. 1 1
      apps/price_pusher/src/options.ts
  95. 15 7
      apps/price_pusher/src/price-config.ts
  96. 37 34
      apps/price_pusher/src/pyth-price-listener.ts
  97. 12 11
      apps/price_pusher/src/solana/balance-tracker.ts
  98. 42 28
      apps/price_pusher/src/solana/command.ts
  99. 49 47
      apps/price_pusher/src/solana/solana.ts
  100. 12 11
      apps/price_pusher/src/sui/balance-tracker.ts

+ 2 - 1
.gitignore

@@ -25,4 +25,5 @@ __pycache__
 .turbo/
 .cursorrules
 .corepack
-justfile
+justfile
+dist/

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

@@ -1 +1,3 @@
 .env*.local
+
+dist/

+ 6 - 0
apps/api-reference/.prettierignore

@@ -6,3 +6,9 @@ node_modules/
 .env*.local
 .env
 .DS_Store
+dist/
+lib/
+build/
+node_modules/
+package.json
+tsconfig*.json

+ 3 - 1
apps/api-reference/jest.config.js

@@ -1 +1,3 @@
-export { nextjs as default } from "@cprussin/jest-config/next";
+import { defineJestConfigForNextJs } from "@pythnetwork/jest-config/define-next-config";
+
+export default defineJestConfigForNextJs();

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

@@ -4,7 +4,7 @@
   "private": true,
   "type": "module",
   "engines": {
-    "node": "22"
+    "node": ">=22.14.0"
   },
   "scripts": {
     "build:vercel": "next build",
@@ -47,7 +47,7 @@
   "devDependencies": {
     "@axe-core/react": "catalog:",
     "@cprussin/eslint-config": "catalog:",
-    "@cprussin/jest-config": "catalog:",
+    "@pythnetwork/jest-config": "workspace:",
     "@cprussin/prettier-config": "catalog:",
     "@cprussin/tsconfig": "catalog:",
     "@svgr/webpack": "catalog:",
@@ -62,7 +62,6 @@
     "postcss": "catalog:",
     "prettier": "catalog:",
     "tailwindcss": "catalog:",
-    "typescript": "catalog:",
     "vercel": "catalog:"
   }
-}
+}

+ 2 - 0
apps/argus/.gitignore

@@ -2,3 +2,5 @@
 *config.yaml
 *secret*
 *private-key*
+
+dist/

+ 2 - 1
apps/developer-hub/.gitignore

@@ -1,2 +1,3 @@
 .env*.local
-.source
+.source
+dist/

+ 6 - 0
apps/developer-hub/.prettierignore

@@ -5,3 +5,9 @@ node_modules/
 .env*.local
 .env
 .DS_Store
+dist/
+lib/
+build/
+node_modules/
+package.json
+tsconfig*.json

+ 3 - 1
apps/developer-hub/jest.config.js

@@ -1 +1,3 @@
-export { nextjs as default } from "@cprussin/jest-config/next";
+import { defineJestConfigForNextJs } from "@pythnetwork/jest-config/define-next-config";
+
+export default defineJestConfigForNextJs();

+ 3 - 4
apps/developer-hub/package.json

@@ -4,7 +4,7 @@
   "private": true,
   "type": "module",
   "engines": {
-    "node": "22"
+    "node": ">=22.14.0"
   },
   "scripts": {
     "build": "next build",
@@ -51,7 +51,7 @@
   },
   "devDependencies": {
     "@cprussin/eslint-config": "catalog:",
-    "@cprussin/jest-config": "catalog:",
+    "@pythnetwork/jest-config": "workspace:",
     "@cprussin/prettier-config": "catalog:",
     "@cprussin/tsconfig": "catalog:",
     "@svgr/webpack": "catalog:",
@@ -70,7 +70,6 @@
     "stylelint": "catalog:",
     "stylelint-config-standard-scss": "catalog:",
     "tailwindcss": "^4.1.6",
-    "typescript": "catalog:",
     "vercel": "catalog:"
   }
-}
+}

+ 0 - 1
apps/entropy-debugger/.gitignore

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

+ 0 - 7
apps/entropy-debugger/.prettierignore

@@ -1,7 +0,0 @@
-.next/
-coverage/
-node_modules/
-*.tsbuildinfo
-.env*.local
-.env
-.DS_Store

+ 0 - 0
apps/entropy-debugger/README.md


+ 0 - 14
apps/entropy-debugger/components.json

@@ -1,14 +0,0 @@
-{
-  "$schema": "https://ui.shadcn.com/schema.json",
-  "style": "default",
-  "rsc": true,
-  "tsx": true,
-  "tailwind": {
-    "config": "tailwind.config.ts",
-    "css": "src/app/globals.css",
-    "baseColor": "neutral",
-    "cssVariables": true,
-    "prefix": ""
-  },
-  "iconLibrary": "lucide"
-}

+ 0 - 1
apps/entropy-debugger/eslint.config.js

@@ -1 +0,0 @@
-export { nextjs as default } from "@cprussin/eslint-config";

+ 0 - 1
apps/entropy-debugger/jest.config.js

@@ -1 +0,0 @@
-export { nextjs as default } from "@cprussin/jest-config/next";

+ 0 - 6
apps/entropy-debugger/next-env.d.ts

@@ -1,6 +0,0 @@
-/// <reference types="next" />
-/// <reference types="next/image-types/global" />
-/// <reference path="./.next/types/routes.d.ts" />
-
-// NOTE: This file should not be edited
-// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

+ 0 - 49
apps/entropy-debugger/next.config.js

@@ -1,49 +0,0 @@
-const config = {
-  reactStrictMode: true,
-
-  pageExtensions: ["ts", "tsx", "mdx"],
-
-  logging: {
-    fetches: {
-      fullUrl: true,
-    },
-  },
-
-  webpack(config) {
-    config.resolve.extensionAlias = {
-      ".js": [".js", ".ts", ".tsx"],
-    };
-
-    return config;
-  },
-
-  headers: async () => [
-    {
-      source: "/:path*",
-      headers: [
-        {
-          key: "X-XSS-Protection",
-          value: "1; mode=block",
-        },
-        {
-          key: "Referrer-Policy",
-          value: "strict-origin-when-cross-origin",
-        },
-        {
-          key: "Strict-Transport-Security",
-          value: "max-age=2592000",
-        },
-        {
-          key: "X-Content-Type-Options",
-          value: "nosniff",
-        },
-        {
-          key: "Permissions-Policy",
-          value:
-            "vibrate=(), geolocation=(), midi=(), notifications=(), push=(), sync-xhr=(), microphone=(), camera=(), magnetometer=(), gyroscope=(), speaker=(), vibrate=(), fullscreen=self",
-        },
-      ],
-    },
-  ],
-};
-export default config;

+ 0 - 52
apps/entropy-debugger/package.json

@@ -1,52 +0,0 @@
-{
-  "name": "@pythnetwork/entropy-debugger",
-  "version": "0.0.0",
-  "private": true,
-  "type": "module",
-  "engines": {
-    "node": "22"
-  },
-  "scripts": {
-    "build": "next build",
-    "fix:format": "prettier --write .",
-    "fix:lint": "eslint --fix . --max-warnings 0",
-    "start:dev": "next dev --port 3005",
-    "start:prod": "next start --port 3005",
-    "test:format": "prettier --check .",
-    "test:lint": "eslint . --max-warnings 0",
-    "test:types": "tsc"
-  },
-  "dependencies": {
-    "@radix-ui/react-select": "catalog:",
-    "@radix-ui/react-slot": "catalog:",
-    "@radix-ui/react-switch": "catalog:",
-    "class-variance-authority": "catalog:",
-    "clsx": "catalog:",
-    "highlight.js": "catalog:",
-    "lucide-react": "catalog:",
-    "next": "catalog:",
-    "react": "catalog:",
-    "react-dom": "catalog:",
-    "tailwind-merge": "catalog:",
-    "tailwindcss-animate": "catalog:",
-    "viem": "catalog:",
-    "zod": "catalog:"
-  },
-  "devDependencies": {
-    "@cprussin/eslint-config": "catalog:",
-    "@cprussin/jest-config": "catalog:",
-    "@cprussin/prettier-config": "catalog:",
-    "@cprussin/tsconfig": "catalog:",
-    "@types/jest": "catalog:",
-    "@types/node": "catalog:",
-    "@types/react": "catalog:",
-    "@types/react-dom": "catalog:",
-    "eslint": "catalog:",
-    "jest": "catalog:",
-    "postcss": "catalog:",
-    "prettier": "catalog:",
-    "tailwindcss": "catalog:",
-    "typescript": "catalog:",
-    "vercel": "catalog:"
-  }
-}

+ 0 - 8
apps/entropy-debugger/postcss.config.js

@@ -1,8 +0,0 @@
-/** @type {import('postcss-load-config').Config} */
-const config = {
-  plugins: {
-    tailwindcss: {},
-  },
-};
-
-export default config;

+ 0 - 1
apps/entropy-debugger/prettier.config.js

@@ -1 +0,0 @@
-export { base as default } from "@cprussin/prettier-config";

+ 0 - 7
apps/entropy-debugger/prettierignore

@@ -1,7 +0,0 @@
-.next/
-coverage/
-node_modules/
-*.tsbuildinfo
-.env*.local
-.env
-.DS_Store

BIN
apps/entropy-debugger/src/app/favicon.ico


BIN
apps/entropy-debugger/src/app/fonts/GeistMonoVF.woff


BIN
apps/entropy-debugger/src/app/fonts/GeistVF.woff


+ 0 - 72
apps/entropy-debugger/src/app/globals.css

@@ -1,72 +0,0 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-
-body {
-  font-family: Arial, Helvetica, sans-serif;
-}
-
-@layer base {
-  :root {
-    --background: 0 0% 100%;
-    --foreground: 0 0% 3.9%;
-    --card: 0 0% 100%;
-    --card-foreground: 0 0% 3.9%;
-    --popover: 0 0% 100%;
-    --popover-foreground: 0 0% 3.9%;
-    --primary: 0 0% 9%;
-    --primary-foreground: 0 0% 98%;
-    --secondary: 0 0% 96.1%;
-    --secondary-foreground: 0 0% 9%;
-    --muted: 0 0% 96.1%;
-    --muted-foreground: 0 0% 45.1%;
-    --accent: 0 0% 96.1%;
-    --accent-foreground: 0 0% 9%;
-    --destructive: 0 84.2% 60.2%;
-    --destructive-foreground: 0 0% 98%;
-    --border: 0 0% 89.8%;
-    --input: 0 0% 89.8%;
-    --ring: 0 0% 3.9%;
-    --chart-1: 12 76% 61%;
-    --chart-2: 173 58% 39%;
-    --chart-3: 197 37% 24%;
-    --chart-4: 43 74% 66%;
-    --chart-5: 27 87% 67%;
-    --radius: 0.5rem;
-  }
-  .dark {
-    --background: 0 0% 3.9%;
-    --foreground: 0 0% 98%;
-    --card: 0 0% 3.9%;
-    --card-foreground: 0 0% 98%;
-    --popover: 0 0% 3.9%;
-    --popover-foreground: 0 0% 98%;
-    --primary: 0 0% 98%;
-    --primary-foreground: 0 0% 9%;
-    --secondary: 0 0% 14.9%;
-    --secondary-foreground: 0 0% 98%;
-    --muted: 0 0% 14.9%;
-    --muted-foreground: 0 0% 63.9%;
-    --accent: 0 0% 14.9%;
-    --accent-foreground: 0 0% 98%;
-    --destructive: 0 62.8% 30.6%;
-    --destructive-foreground: 0 0% 98%;
-    --border: 0 0% 14.9%;
-    --input: 0 0% 14.9%;
-    --ring: 0 0% 83.1%;
-    --chart-1: 220 70% 50%;
-    --chart-2: 160 60% 45%;
-    --chart-3: 30 80% 55%;
-    --chart-4: 280 65% 60%;
-    --chart-5: 340 75% 55%;
-  }
-}
-
-@layer base {
-  * {
-    @apply border-border;
-  }
-  body {
-    @apply bg-background text-foreground;
-  }
-}

+ 0 - 35
apps/entropy-debugger/src/app/layout.tsx

@@ -1,35 +0,0 @@
-import type { Metadata } from "next";
-import localFont from "next/font/local";
-import "./globals.css";
-
-const geistSans = localFont({
-  src: "./fonts/GeistVF.woff",
-  variable: "--font-geist-sans",
-  weight: "100 900",
-});
-const geistMono = localFont({
-  src: "./fonts/GeistMonoVF.woff",
-  variable: "--font-geist-mono",
-  weight: "100 900",
-});
-
-export const metadata: Metadata = {
-  title: "Pyth Entropy Debug App",
-  description: "Pyth Entropy Debug App",
-};
-
-export default function RootLayout({
-  children,
-}: Readonly<{
-  children: React.ReactNode;
-}>) {
-  return (
-    <html lang="en">
-      <body
-        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
-      >
-        {children}
-      </body>
-    </html>
-  );
-}

+ 0 - 240
apps/entropy-debugger/src/app/page.tsx

@@ -1,240 +0,0 @@
-"use client";
-
-import hljs from "highlight.js/lib/core";
-import bash from "highlight.js/lib/languages/bash";
-import { useState, useMemo, useCallback, useEffect, useRef } from "react";
-
-import { Input } from "../components/ui/input";
-import {
-  Select,
-  SelectContent,
-  SelectItem,
-  SelectTrigger,
-  SelectValue,
-} from "../components/ui/select";
-import { Switch } from "../components/ui/switch";
-import { requestCallback } from "../lib/revelation";
-import {
-  EntropyDeployments,
-  isValidDeployment,
-} from "../store/entropy-deployments";
-
-import "highlight.js/styles/github-dark.css"; // You can choose different themes
-
-// Register the bash language
-hljs.registerLanguage("bash", bash);
-
-class BaseError extends Error {
-  constructor(message: string) {
-    super(message);
-    this.name = "BaseError";
-  }
-}
-
-class InvalidTxHashError extends BaseError {
-  constructor(message: string) {
-    super(message);
-    this.name = "InvalidTxHashError";
-  }
-}
-
-enum TxStateType {
-  NotLoaded,
-  Loading,
-  Success,
-  Error,
-}
-
-const TxState = {
-  NotLoaded: () => ({ status: TxStateType.NotLoaded as const }),
-  Loading: () => ({ status: TxStateType.Loading as const }),
-  Success: (data: string) => ({ status: TxStateType.Success as const, data }),
-  ErrorState: (error: unknown) => ({
-    status: TxStateType.Error as const,
-    error,
-  }),
-};
-
-type TxStateContext =
-  | ReturnType<typeof TxState.NotLoaded>
-  | ReturnType<typeof TxState.Loading>
-  | ReturnType<typeof TxState.Success>
-  | ReturnType<typeof TxState.ErrorState>;
-
-export default function PythEntropyDebugApp() {
-  const [state, setState] = useState<TxStateContext>(TxState.NotLoaded());
-  const [isMainnet, setIsMainnet] = useState<boolean>(false);
-  const [txHash, setTxHash] = useState<string>("");
-  const [error, setError] = useState<BaseError | undefined>(undefined);
-  const [selectedChain, setSelectedChain] = useState<
-    "" | keyof typeof EntropyDeployments
-  >("");
-
-  const validateTxHash = (hash: string) => {
-    if (!isValidTxHash(hash) && hash !== "") {
-      setError(
-        new InvalidTxHashError(
-          "Transaction hash must be 64 hexadecimal characters",
-        ),
-      );
-    } else {
-      setError(undefined);
-    }
-    setTxHash(hash);
-  };
-
-  const availableChains = useMemo(() => {
-    return Object.entries(EntropyDeployments)
-      .filter(
-        ([, deployment]) =>
-          deployment.network === (isMainnet ? "mainnet" : "testnet"),
-      )
-      .toSorted(([a], [b]) => a.localeCompare(b))
-      .map(([key]) => key);
-  }, [isMainnet]);
-
-  const oncClickFetchInfo = useCallback(() => {
-    if (selectedChain !== "") {
-      setState(TxState.Loading());
-      requestCallback(txHash, selectedChain)
-        .then((data) => {
-          setState(TxState.Success(data));
-        })
-        .catch((error: unknown) => {
-          setState(TxState.ErrorState(error));
-        });
-    }
-  }, [txHash, selectedChain]);
-
-  const updateIsMainnet = useCallback(
-    (newValue: boolean) => {
-      setSelectedChain("");
-      setIsMainnet(newValue);
-    },
-    [setSelectedChain, setIsMainnet],
-  );
-
-  const updateSelectedChain = useCallback(
-    (chain: string) => {
-      if (isValidDeployment(chain)) {
-        setSelectedChain(chain);
-      }
-    },
-    [setSelectedChain],
-  );
-
-  return (
-    <div className="flex flex-col items-center justify-start h-screen">
-      <h1 className="text-4xl font-bold mt-8">Pyth Entropy Debug App</h1>
-
-      <div className="flex items-center space-x-2 mt-4">
-        <label htmlFor="network-mode">Testnet</label>
-        <Switch
-          id="network-mode"
-          defaultChecked={false}
-          onCheckedChange={updateIsMainnet}
-        />
-        <label htmlFor="network-mode">Mainnet</label>
-      </div>
-      <div className="mt-4">
-        <Select onValueChange={updateSelectedChain} value={selectedChain}>
-          <SelectTrigger>
-            <SelectValue placeholder="Select Chain" />
-          </SelectTrigger>
-          <SelectContent>
-            {availableChains.map((chain) => (
-              <SelectItem key={chain} value={chain}>
-                {chain.charAt(0).toUpperCase() +
-                  chain.slice(1).replaceAll("-", " ")}
-              </SelectItem>
-            ))}
-          </SelectContent>
-        </Select>
-      </div>
-      <div className="mt-4">
-        <label htmlFor="tx-hash" className="mr-2">
-          Request Transaction Hash:
-        </label>
-        <Input
-          minLength={64}
-          id="tx-hash"
-          className={`border rounded p-2 w-full ${error ? "border-red-500" : ""}`}
-          placeholder="Enter transaction hash"
-          value={txHash}
-          onChange={(e) => {
-            validateTxHash(e.target.value);
-          }}
-        />
-        {error && <p className="text-red-500 text-sm mt-1">{error.message}</p>}
-      </div>
-      <div className="mt-4">
-        <button
-          className="bg-blue-500 hover:bg-blue-600 text-white p-2 rounded disabled:bg-slate-200 disabled:text-slate-500 disabled:cursor-not-allowed"
-          onClick={oncClickFetchInfo}
-          disabled={selectedChain === "" || txHash === ""}
-        >
-          Fetch Info
-        </button>
-      </div>
-      <Info state={state} />
-    </div>
-  );
-}
-
-const Info = ({ state }: { state: TxStateContext }) => {
-  const preRef = useRef<HTMLPreElement>(null);
-
-  useEffect(() => {
-    if (preRef.current && state.status === TxStateType.Success) {
-      hljs.highlightElement(preRef.current);
-    }
-  }, [state]);
-
-  switch (state.status) {
-    case TxStateType.NotLoaded: {
-      return <div>Not loaded</div>;
-    }
-    case TxStateType.Loading: {
-      return <div>Loading...</div>;
-    }
-    case TxStateType.Success: {
-      return (
-        <div className="mt-4 p-4 bg-gray-100 rounded w-full max-w-3xl">
-          <p className="mb-2">
-            Please run the following command in your terminal:
-          </p>
-          <div className="relative">
-            <pre
-              ref={preRef}
-              className="bg-black text-white p-4 rounded overflow-x-auto whitespace-pre-wrap break-words"
-            >
-              <code className="language-bash">{state.data}</code>
-            </pre>
-            <button
-              onClick={() => {
-                navigator.clipboard.writeText(state.data).catch(() => {
-                  /* no-op on error */
-                });
-              }}
-              className="absolute top-2 right-2 bg-gray-700 text-white px-3 py-1 rounded hover:bg-gray-600"
-            >
-              Copy
-            </button>
-          </div>
-        </div>
-      );
-    }
-    case TxStateType.Error: {
-      return (
-        <div className="mt-4 p-4 bg-red-100 border border-red-400 rounded">
-          <div className="text-red-600">{String(state.error)}</div>
-        </div>
-      );
-    }
-  }
-};
-
-function isValidTxHash(hash: string) {
-  const cleanHash = hash.toLowerCase().replace("0x", "");
-  return /^[\da-f]{64}$/.test(cleanHash);
-}

+ 0 - 56
apps/entropy-debugger/src/components/ui/button.tsx

@@ -1,56 +0,0 @@
-import { Slot } from "@radix-ui/react-slot";
-import type { VariantProps } from "class-variance-authority";
-import { cva } from "class-variance-authority";
-import type { ComponentProps } from "react";
-
-import { cn } from "../../lib/utils";
-
-const buttonVariants = cva(
-  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
-  {
-    variants: {
-      variant: {
-        default: "bg-primary text-primary-foreground hover:bg-primary/90",
-        destructive:
-          "bg-destructive text-destructive-foreground hover:bg-destructive/90",
-        outline:
-          "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
-        secondary:
-          "bg-secondary text-secondary-foreground hover:bg-secondary/80",
-        ghost: "hover:bg-accent hover:text-accent-foreground",
-        link: "text-primary underline-offset-4 hover:underline",
-      },
-      size: {
-        default: "h-10 px-4 py-2",
-        sm: "h-9 rounded-md px-3",
-        lg: "h-11 rounded-md px-8",
-        icon: "h-10 w-10",
-      },
-    },
-    defaultVariants: {
-      variant: "default",
-      size: "default",
-    },
-  },
-);
-
-export type ButtonProps = {
-  asChild?: boolean;
-} & ComponentProps<"button"> &
-  VariantProps<typeof buttonVariants>;
-
-export const Button = ({
-  className,
-  variant,
-  size,
-  asChild = false,
-  ...props
-}: ButtonProps) => {
-  const Comp = asChild ? Slot : "button";
-  return (
-    <Comp
-      className={cn(buttonVariants({ variant, size, className }))}
-      {...props}
-    />
-  );
-};

+ 0 - 13
apps/entropy-debugger/src/components/ui/input.tsx

@@ -1,13 +0,0 @@
-import type { ComponentProps } from "react";
-
-import { cn } from "../../lib/utils";
-
-export const Input = ({ className, ...props }: ComponentProps<"input">) => (
-  <input
-    className={cn(
-      "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
-      className,
-    )}
-    {...props}
-  />
-);

+ 0 - 146
apps/entropy-debugger/src/components/ui/select.tsx

@@ -1,146 +0,0 @@
-"use client";
-
-import {
-  Trigger,
-  ScrollUpButton,
-  ScrollDownButton,
-  Icon,
-  Portal,
-  Content,
-  Viewport,
-  Label,
-  ItemIndicator,
-  ItemText,
-  Item,
-  Separator,
-} from "@radix-ui/react-select";
-import { Check, ChevronDown, ChevronUp } from "lucide-react";
-import type { ComponentProps } from "react";
-
-import { cn } from "../../lib/utils";
-
-export {
-  Root as Select,
-  Group as SelectGroup,
-  Value as SelectValue,
-} from "@radix-ui/react-select";
-
-export const SelectTrigger = ({
-  className,
-  children,
-  ...props
-}: ComponentProps<typeof Trigger>) => (
-  <Trigger
-    className={cn(
-      "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
-      className,
-    )}
-    {...props}
-  >
-    {children}
-    <Icon asChild>
-      <ChevronDown className="h-4 w-4 opacity-50" />
-    </Icon>
-  </Trigger>
-);
-
-export const SelectScrollUpButton = ({
-  className,
-  ...props
-}: ComponentProps<typeof ScrollUpButton>) => (
-  <ScrollUpButton
-    className={cn(
-      "flex cursor-default items-center justify-center py-1",
-      className,
-    )}
-    {...props}
-  >
-    <ChevronUp className="h-4 w-4" />
-  </ScrollUpButton>
-);
-
-export const SelectScrollDownButton = ({
-  className,
-  ...props
-}: ComponentProps<typeof ScrollDownButton>) => (
-  <ScrollDownButton
-    className={cn(
-      "flex cursor-default items-center justify-center py-1",
-      className,
-    )}
-    {...props}
-  >
-    <ChevronDown className="h-4 w-4" />
-  </ScrollDownButton>
-);
-
-export const SelectContent = ({
-  className,
-  children,
-  position = "popper",
-  ...props
-}: ComponentProps<typeof Content>) => (
-  <Portal>
-    <Content
-      className={cn(
-        "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
-        position === "popper" &&
-          "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
-        className,
-      )}
-      position={position}
-      {...props}
-    >
-      <SelectScrollUpButton />
-      <Viewport
-        className={cn(
-          "p-1",
-          position === "popper" &&
-            "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
-        )}
-      >
-        {children}
-      </Viewport>
-      <SelectScrollDownButton />
-    </Content>
-  </Portal>
-);
-
-export const SelectLabel = ({
-  className,
-  ...props
-}: ComponentProps<typeof Label>) => (
-  <Label
-    className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
-    {...props}
-  />
-);
-
-export const SelectItem = ({
-  className,
-  children,
-  ...props
-}: ComponentProps<typeof Item>) => (
-  <Item
-    className={cn(
-      "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
-      className,
-    )}
-    {...props}
-  >
-    <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
-      <ItemIndicator>
-        <Check className="h-4 w-4" />
-      </ItemIndicator>
-    </span>
-
-    <ItemText>{children}</ItemText>
-  </Item>
-);
-
-export const SelectSeparator = ({
-  className,
-  ...props
-}: ComponentProps<typeof Separator>) => (
-  <Separator className={cn("-mx-1 my-1 h-px bg-muted", className)} {...props} />
-);

+ 0 - 25
apps/entropy-debugger/src/components/ui/switch.tsx

@@ -1,25 +0,0 @@
-"use client";
-
-import { Root, Thumb } from "@radix-ui/react-switch";
-import type { ComponentProps } from "react";
-
-import { cn } from "../../lib/utils";
-
-export const Switch = ({
-  className,
-  ...props
-}: ComponentProps<typeof Root>) => (
-  <Root
-    className={cn(
-      "peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
-      className,
-    )}
-    {...props}
-  >
-    <Thumb
-      className={cn(
-        "pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0",
-      )}
-    />
-  </Root>
-);

+ 0 - 1000
apps/entropy-debugger/src/lib/entropy-abi.ts

@@ -1,1000 +0,0 @@
-export const EntropyAbi = [
-  {
-    anonymous: false,
-    inputs: [
-      {
-        indexed: false,
-        internalType: "address",
-        name: "provider",
-        type: "address",
-      },
-      {
-        indexed: false,
-        internalType: "address",
-        name: "oldFeeManager",
-        type: "address",
-      },
-      {
-        indexed: false,
-        internalType: "address",
-        name: "newFeeManager",
-        type: "address",
-      },
-    ],
-    name: "ProviderFeeManagerUpdated",
-    type: "event",
-  },
-  {
-    anonymous: false,
-    inputs: [
-      {
-        indexed: false,
-        internalType: "address",
-        name: "provider",
-        type: "address",
-      },
-      {
-        indexed: false,
-        internalType: "uint128",
-        name: "oldFee",
-        type: "uint128",
-      },
-      {
-        indexed: false,
-        internalType: "uint128",
-        name: "newFee",
-        type: "uint128",
-      },
-    ],
-    name: "ProviderFeeUpdated",
-    type: "event",
-  },
-  {
-    anonymous: false,
-    inputs: [
-      {
-        indexed: false,
-        internalType: "address",
-        name: "provider",
-        type: "address",
-      },
-      {
-        indexed: false,
-        internalType: "uint32",
-        name: "oldMaxNumHashes",
-        type: "uint32",
-      },
-      {
-        indexed: false,
-        internalType: "uint32",
-        name: "newMaxNumHashes",
-        type: "uint32",
-      },
-    ],
-    name: "ProviderMaxNumHashesAdvanced",
-    type: "event",
-  },
-  {
-    anonymous: false,
-    inputs: [
-      {
-        indexed: false,
-        internalType: "address",
-        name: "provider",
-        type: "address",
-      },
-      {
-        indexed: false,
-        internalType: "bytes",
-        name: "oldUri",
-        type: "bytes",
-      },
-      {
-        indexed: false,
-        internalType: "bytes",
-        name: "newUri",
-        type: "bytes",
-      },
-    ],
-    name: "ProviderUriUpdated",
-    type: "event",
-  },
-  {
-    anonymous: false,
-    inputs: [
-      {
-        components: [
-          {
-            internalType: "uint128",
-            name: "feeInWei",
-            type: "uint128",
-          },
-          {
-            internalType: "uint128",
-            name: "accruedFeesInWei",
-            type: "uint128",
-          },
-          {
-            internalType: "bytes32",
-            name: "originalCommitment",
-            type: "bytes32",
-          },
-          {
-            internalType: "uint64",
-            name: "originalCommitmentSequenceNumber",
-            type: "uint64",
-          },
-          {
-            internalType: "bytes",
-            name: "commitmentMetadata",
-            type: "bytes",
-          },
-          {
-            internalType: "bytes",
-            name: "uri",
-            type: "bytes",
-          },
-          {
-            internalType: "uint64",
-            name: "endSequenceNumber",
-            type: "uint64",
-          },
-          {
-            internalType: "uint64",
-            name: "sequenceNumber",
-            type: "uint64",
-          },
-          {
-            internalType: "bytes32",
-            name: "currentCommitment",
-            type: "bytes32",
-          },
-          {
-            internalType: "uint64",
-            name: "currentCommitmentSequenceNumber",
-            type: "uint64",
-          },
-          {
-            internalType: "address",
-            name: "feeManager",
-            type: "address",
-          },
-          {
-            internalType: "uint32",
-            name: "maxNumHashes",
-            type: "uint32",
-          },
-        ],
-        indexed: false,
-        internalType: "struct EntropyStructs.ProviderInfo",
-        name: "provider",
-        type: "tuple",
-      },
-    ],
-    name: "Registered",
-    type: "event",
-  },
-  {
-    anonymous: false,
-    inputs: [
-      {
-        components: [
-          {
-            internalType: "address",
-            name: "provider",
-            type: "address",
-          },
-          {
-            internalType: "uint64",
-            name: "sequenceNumber",
-            type: "uint64",
-          },
-          {
-            internalType: "uint32",
-            name: "numHashes",
-            type: "uint32",
-          },
-          {
-            internalType: "bytes32",
-            name: "commitment",
-            type: "bytes32",
-          },
-          {
-            internalType: "uint64",
-            name: "blockNumber",
-            type: "uint64",
-          },
-          {
-            internalType: "address",
-            name: "requester",
-            type: "address",
-          },
-          {
-            internalType: "bool",
-            name: "useBlockhash",
-            type: "bool",
-          },
-          {
-            internalType: "bool",
-            name: "isRequestWithCallback",
-            type: "bool",
-          },
-        ],
-        indexed: false,
-        internalType: "struct EntropyStructs.Request",
-        name: "request",
-        type: "tuple",
-      },
-    ],
-    name: "Requested",
-    type: "event",
-  },
-  {
-    anonymous: false,
-    inputs: [
-      {
-        indexed: true,
-        internalType: "address",
-        name: "provider",
-        type: "address",
-      },
-      {
-        indexed: true,
-        internalType: "address",
-        name: "requestor",
-        type: "address",
-      },
-      {
-        indexed: true,
-        internalType: "uint64",
-        name: "sequenceNumber",
-        type: "uint64",
-      },
-      {
-        indexed: false,
-        internalType: "bytes32",
-        name: "userRandomNumber",
-        type: "bytes32",
-      },
-      {
-        components: [
-          {
-            internalType: "address",
-            name: "provider",
-            type: "address",
-          },
-          {
-            internalType: "uint64",
-            name: "sequenceNumber",
-            type: "uint64",
-          },
-          {
-            internalType: "uint32",
-            name: "numHashes",
-            type: "uint32",
-          },
-          {
-            internalType: "bytes32",
-            name: "commitment",
-            type: "bytes32",
-          },
-          {
-            internalType: "uint64",
-            name: "blockNumber",
-            type: "uint64",
-          },
-          {
-            internalType: "address",
-            name: "requester",
-            type: "address",
-          },
-          {
-            internalType: "bool",
-            name: "useBlockhash",
-            type: "bool",
-          },
-          {
-            internalType: "bool",
-            name: "isRequestWithCallback",
-            type: "bool",
-          },
-        ],
-        indexed: false,
-        internalType: "struct EntropyStructs.Request",
-        name: "request",
-        type: "tuple",
-      },
-    ],
-    name: "RequestedWithCallback",
-    type: "event",
-  },
-  {
-    anonymous: false,
-    inputs: [
-      {
-        components: [
-          {
-            internalType: "address",
-            name: "provider",
-            type: "address",
-          },
-          {
-            internalType: "uint64",
-            name: "sequenceNumber",
-            type: "uint64",
-          },
-          {
-            internalType: "uint32",
-            name: "numHashes",
-            type: "uint32",
-          },
-          {
-            internalType: "bytes32",
-            name: "commitment",
-            type: "bytes32",
-          },
-          {
-            internalType: "uint64",
-            name: "blockNumber",
-            type: "uint64",
-          },
-          {
-            internalType: "address",
-            name: "requester",
-            type: "address",
-          },
-          {
-            internalType: "bool",
-            name: "useBlockhash",
-            type: "bool",
-          },
-          {
-            internalType: "bool",
-            name: "isRequestWithCallback",
-            type: "bool",
-          },
-        ],
-        indexed: false,
-        internalType: "struct EntropyStructs.Request",
-        name: "request",
-        type: "tuple",
-      },
-      {
-        indexed: false,
-        internalType: "bytes32",
-        name: "userRevelation",
-        type: "bytes32",
-      },
-      {
-        indexed: false,
-        internalType: "bytes32",
-        name: "providerRevelation",
-        type: "bytes32",
-      },
-      {
-        indexed: false,
-        internalType: "bytes32",
-        name: "blockHash",
-        type: "bytes32",
-      },
-      {
-        indexed: false,
-        internalType: "bytes32",
-        name: "randomNumber",
-        type: "bytes32",
-      },
-    ],
-    name: "Revealed",
-    type: "event",
-  },
-  {
-    anonymous: false,
-    inputs: [
-      {
-        components: [
-          {
-            internalType: "address",
-            name: "provider",
-            type: "address",
-          },
-          {
-            internalType: "uint64",
-            name: "sequenceNumber",
-            type: "uint64",
-          },
-          {
-            internalType: "uint32",
-            name: "numHashes",
-            type: "uint32",
-          },
-          {
-            internalType: "bytes32",
-            name: "commitment",
-            type: "bytes32",
-          },
-          {
-            internalType: "uint64",
-            name: "blockNumber",
-            type: "uint64",
-          },
-          {
-            internalType: "address",
-            name: "requester",
-            type: "address",
-          },
-          {
-            internalType: "bool",
-            name: "useBlockhash",
-            type: "bool",
-          },
-          {
-            internalType: "bool",
-            name: "isRequestWithCallback",
-            type: "bool",
-          },
-        ],
-        indexed: false,
-        internalType: "struct EntropyStructs.Request",
-        name: "request",
-        type: "tuple",
-      },
-      {
-        indexed: false,
-        internalType: "bytes32",
-        name: "userRandomNumber",
-        type: "bytes32",
-      },
-      {
-        indexed: false,
-        internalType: "bytes32",
-        name: "providerRevelation",
-        type: "bytes32",
-      },
-      {
-        indexed: false,
-        internalType: "bytes32",
-        name: "randomNumber",
-        type: "bytes32",
-      },
-    ],
-    name: "RevealedWithCallback",
-    type: "event",
-  },
-  {
-    anonymous: false,
-    inputs: [
-      {
-        indexed: false,
-        internalType: "address",
-        name: "provider",
-        type: "address",
-      },
-      {
-        indexed: false,
-        internalType: "address",
-        name: "recipient",
-        type: "address",
-      },
-      {
-        indexed: false,
-        internalType: "uint128",
-        name: "withdrawnAmount",
-        type: "uint128",
-      },
-    ],
-    name: "Withdrawal",
-    type: "event",
-  },
-  {
-    inputs: [
-      {
-        internalType: "address",
-        name: "provider",
-        type: "address",
-      },
-      {
-        internalType: "uint64",
-        name: "advancedSequenceNumber",
-        type: "uint64",
-      },
-      {
-        internalType: "bytes32",
-        name: "providerRevelation",
-        type: "bytes32",
-      },
-    ],
-    name: "advanceProviderCommitment",
-    outputs: [],
-    stateMutability: "nonpayable",
-    type: "function",
-  },
-  {
-    inputs: [
-      {
-        internalType: "bytes32",
-        name: "userRandomness",
-        type: "bytes32",
-      },
-      {
-        internalType: "bytes32",
-        name: "providerRandomness",
-        type: "bytes32",
-      },
-      {
-        internalType: "bytes32",
-        name: "blockHash",
-        type: "bytes32",
-      },
-    ],
-    name: "combineRandomValues",
-    outputs: [
-      {
-        internalType: "bytes32",
-        name: "combinedRandomness",
-        type: "bytes32",
-      },
-    ],
-    stateMutability: "pure",
-    type: "function",
-  },
-  {
-    inputs: [
-      {
-        internalType: "bytes32",
-        name: "userRandomness",
-        type: "bytes32",
-      },
-    ],
-    name: "constructUserCommitment",
-    outputs: [
-      {
-        internalType: "bytes32",
-        name: "userCommitment",
-        type: "bytes32",
-      },
-    ],
-    stateMutability: "pure",
-    type: "function",
-  },
-  {
-    inputs: [],
-    name: "getAccruedPythFees",
-    outputs: [
-      {
-        internalType: "uint128",
-        name: "accruedPythFeesInWei",
-        type: "uint128",
-      },
-    ],
-    stateMutability: "view",
-    type: "function",
-  },
-  {
-    inputs: [],
-    name: "getDefaultProvider",
-    outputs: [
-      {
-        internalType: "address",
-        name: "provider",
-        type: "address",
-      },
-    ],
-    stateMutability: "view",
-    type: "function",
-  },
-  {
-    inputs: [
-      {
-        internalType: "address",
-        name: "provider",
-        type: "address",
-      },
-    ],
-    name: "getFee",
-    outputs: [
-      {
-        internalType: "uint128",
-        name: "feeAmount",
-        type: "uint128",
-      },
-    ],
-    stateMutability: "view",
-    type: "function",
-  },
-  {
-    inputs: [
-      {
-        internalType: "address",
-        name: "provider",
-        type: "address",
-      },
-    ],
-    name: "getProviderInfo",
-    outputs: [
-      {
-        components: [
-          {
-            internalType: "uint128",
-            name: "feeInWei",
-            type: "uint128",
-          },
-          {
-            internalType: "uint128",
-            name: "accruedFeesInWei",
-            type: "uint128",
-          },
-          {
-            internalType: "bytes32",
-            name: "originalCommitment",
-            type: "bytes32",
-          },
-          {
-            internalType: "uint64",
-            name: "originalCommitmentSequenceNumber",
-            type: "uint64",
-          },
-          {
-            internalType: "bytes",
-            name: "commitmentMetadata",
-            type: "bytes",
-          },
-          {
-            internalType: "bytes",
-            name: "uri",
-            type: "bytes",
-          },
-          {
-            internalType: "uint64",
-            name: "endSequenceNumber",
-            type: "uint64",
-          },
-          {
-            internalType: "uint64",
-            name: "sequenceNumber",
-            type: "uint64",
-          },
-          {
-            internalType: "bytes32",
-            name: "currentCommitment",
-            type: "bytes32",
-          },
-          {
-            internalType: "uint64",
-            name: "currentCommitmentSequenceNumber",
-            type: "uint64",
-          },
-          {
-            internalType: "address",
-            name: "feeManager",
-            type: "address",
-          },
-          {
-            internalType: "uint32",
-            name: "maxNumHashes",
-            type: "uint32",
-          },
-        ],
-        internalType: "struct EntropyStructs.ProviderInfo",
-        name: "info",
-        type: "tuple",
-      },
-    ],
-    stateMutability: "view",
-    type: "function",
-  },
-  {
-    inputs: [
-      {
-        internalType: "address",
-        name: "provider",
-        type: "address",
-      },
-      {
-        internalType: "uint64",
-        name: "sequenceNumber",
-        type: "uint64",
-      },
-    ],
-    name: "getRequest",
-    outputs: [
-      {
-        components: [
-          {
-            internalType: "address",
-            name: "provider",
-            type: "address",
-          },
-          {
-            internalType: "uint64",
-            name: "sequenceNumber",
-            type: "uint64",
-          },
-          {
-            internalType: "uint32",
-            name: "numHashes",
-            type: "uint32",
-          },
-          {
-            internalType: "bytes32",
-            name: "commitment",
-            type: "bytes32",
-          },
-          {
-            internalType: "uint64",
-            name: "blockNumber",
-            type: "uint64",
-          },
-          {
-            internalType: "address",
-            name: "requester",
-            type: "address",
-          },
-          {
-            internalType: "bool",
-            name: "useBlockhash",
-            type: "bool",
-          },
-          {
-            internalType: "bool",
-            name: "isRequestWithCallback",
-            type: "bool",
-          },
-        ],
-        internalType: "struct EntropyStructs.Request",
-        name: "req",
-        type: "tuple",
-      },
-    ],
-    stateMutability: "view",
-    type: "function",
-  },
-  {
-    inputs: [
-      {
-        internalType: "uint128",
-        name: "feeInWei",
-        type: "uint128",
-      },
-      {
-        internalType: "bytes32",
-        name: "commitment",
-        type: "bytes32",
-      },
-      {
-        internalType: "bytes",
-        name: "commitmentMetadata",
-        type: "bytes",
-      },
-      {
-        internalType: "uint64",
-        name: "chainLength",
-        type: "uint64",
-      },
-      {
-        internalType: "bytes",
-        name: "uri",
-        type: "bytes",
-      },
-    ],
-    name: "register",
-    outputs: [],
-    stateMutability: "nonpayable",
-    type: "function",
-  },
-  {
-    inputs: [
-      {
-        internalType: "address",
-        name: "provider",
-        type: "address",
-      },
-      {
-        internalType: "bytes32",
-        name: "userCommitment",
-        type: "bytes32",
-      },
-      {
-        internalType: "bool",
-        name: "useBlockHash",
-        type: "bool",
-      },
-    ],
-    name: "request",
-    outputs: [
-      {
-        internalType: "uint64",
-        name: "assignedSequenceNumber",
-        type: "uint64",
-      },
-    ],
-    stateMutability: "payable",
-    type: "function",
-  },
-  {
-    inputs: [
-      {
-        internalType: "address",
-        name: "provider",
-        type: "address",
-      },
-      {
-        internalType: "bytes32",
-        name: "userRandomNumber",
-        type: "bytes32",
-      },
-    ],
-    name: "requestWithCallback",
-    outputs: [
-      {
-        internalType: "uint64",
-        name: "assignedSequenceNumber",
-        type: "uint64",
-      },
-    ],
-    stateMutability: "payable",
-    type: "function",
-  },
-  {
-    inputs: [
-      {
-        internalType: "address",
-        name: "provider",
-        type: "address",
-      },
-      {
-        internalType: "uint64",
-        name: "sequenceNumber",
-        type: "uint64",
-      },
-      {
-        internalType: "bytes32",
-        name: "userRevelation",
-        type: "bytes32",
-      },
-      {
-        internalType: "bytes32",
-        name: "providerRevelation",
-        type: "bytes32",
-      },
-    ],
-    name: "reveal",
-    outputs: [
-      {
-        internalType: "bytes32",
-        name: "randomNumber",
-        type: "bytes32",
-      },
-    ],
-    stateMutability: "nonpayable",
-    type: "function",
-  },
-  {
-    inputs: [
-      {
-        internalType: "address",
-        name: "provider",
-        type: "address",
-      },
-      {
-        internalType: "uint64",
-        name: "sequenceNumber",
-        type: "uint64",
-      },
-      {
-        internalType: "bytes32",
-        name: "userRandomNumber",
-        type: "bytes32",
-      },
-      {
-        internalType: "bytes32",
-        name: "providerRevelation",
-        type: "bytes32",
-      },
-    ],
-    name: "revealWithCallback",
-    outputs: [],
-    stateMutability: "nonpayable",
-    type: "function",
-  },
-  {
-    inputs: [
-      {
-        internalType: "address",
-        name: "manager",
-        type: "address",
-      },
-    ],
-    name: "setFeeManager",
-    outputs: [],
-    stateMutability: "nonpayable",
-    type: "function",
-  },
-  {
-    inputs: [
-      {
-        internalType: "uint32",
-        name: "maxNumHashes",
-        type: "uint32",
-      },
-    ],
-    name: "setMaxNumHashes",
-    outputs: [],
-    stateMutability: "nonpayable",
-    type: "function",
-  },
-  {
-    inputs: [
-      {
-        internalType: "uint128",
-        name: "newFeeInWei",
-        type: "uint128",
-      },
-    ],
-    name: "setProviderFee",
-    outputs: [],
-    stateMutability: "nonpayable",
-    type: "function",
-  },
-  {
-    inputs: [
-      {
-        internalType: "address",
-        name: "provider",
-        type: "address",
-      },
-      {
-        internalType: "uint128",
-        name: "newFeeInWei",
-        type: "uint128",
-      },
-    ],
-    name: "setProviderFeeAsFeeManager",
-    outputs: [],
-    stateMutability: "nonpayable",
-    type: "function",
-  },
-  {
-    inputs: [
-      {
-        internalType: "bytes",
-        name: "newUri",
-        type: "bytes",
-      },
-    ],
-    name: "setProviderUri",
-    outputs: [],
-    stateMutability: "nonpayable",
-    type: "function",
-  },
-  {
-    inputs: [
-      {
-        internalType: "uint128",
-        name: "amount",
-        type: "uint128",
-      },
-    ],
-    name: "withdraw",
-    outputs: [],
-    stateMutability: "nonpayable",
-    type: "function",
-  },
-  {
-    inputs: [
-      {
-        internalType: "address",
-        name: "provider",
-        type: "address",
-      },
-      {
-        internalType: "uint128",
-        name: "amount",
-        type: "uint128",
-      },
-    ],
-    name: "withdrawAsFeeManager",
-    outputs: [],
-    stateMutability: "nonpayable",
-    type: "function",
-  },
-] as const;

+ 0 - 83
apps/entropy-debugger/src/lib/revelation.ts

@@ -1,83 +0,0 @@
-import { createPublicClient, http, parseEventLogs, publicActions } from "viem";
-import { z } from "zod";
-
-import { EntropyAbi } from "./entropy-abi";
-import type { EntropyDeployment } from "../store/entropy-deployments";
-import { EntropyDeployments } from "../store/entropy-deployments";
-
-export async function requestCallback(
-  txHash: string,
-  chain: keyof typeof EntropyDeployments,
-): Promise<string> {
-  const deployment = EntropyDeployments[chain];
-  const { provider, sequenceNumber, userRandomNumber, blockNumber } =
-    await fetchInfoFromTx(txHash, deployment);
-  const revelation = await getRevelation(
-    chain,
-    blockNumber,
-    Number(sequenceNumber),
-  );
-
-  return `cast send ${deployment.address} 'revealWithCallback(address, uint64, bytes32, bytes32)' ${provider} ${sequenceNumber.toString()} ${userRandomNumber} ${revelation.value.data} -r ${deployment.rpc} --private-key <YOUR_PRIVATE_KEY>`;
-}
-
-export async function fetchInfoFromTx(
-  txHash: string,
-  deployment: EntropyDeployment,
-) {
-  const receipt = await createPublicClient({
-    transport: http(deployment.rpc),
-  })
-    .extend(publicActions)
-    .getTransactionReceipt({
-      hash: txHash as `0x${string}`,
-    });
-
-  const logs = parseEventLogs({
-    abi: EntropyAbi,
-    logs: receipt.logs,
-    eventName: "RequestedWithCallback",
-  });
-
-  const firstLog = logs[0];
-  if (firstLog) {
-    const { provider, sequenceNumber, userRandomNumber } = firstLog.args;
-    return {
-      provider,
-      sequenceNumber,
-      userRandomNumber,
-      blockNumber: receipt.blockNumber,
-    };
-  } else {
-    throw new Error(
-      `No logs found for ${txHash}. Are you sure you send the requestCallback Transaction?`,
-    );
-  }
-}
-
-export async function getRevelation(
-  chain: keyof typeof EntropyDeployments,
-  blockNumber: bigint,
-  sequenceNumber: number,
-) {
-  const deployment = EntropyDeployments[chain];
-  const url = new URL(
-    `/v1/chains/${chain}/revelations/${sequenceNumber.toString()}?block_number=${blockNumber.toString()}`,
-    deployment.network === "mainnet"
-      ? "https://fortuna.dourolabs.app"
-      : "https://fortuna-staging.dourolabs.app",
-  );
-  const response = await fetch(url);
-
-  if (response.ok) {
-    return revelationSchema.parse(await response.json());
-  } else {
-    throw new Error(`The provider returned an error: ${await response.text()}`);
-  }
-}
-
-const revelationSchema = z.object({
-  value: z.object({
-    data: z.string(),
-  }),
-});

+ 0 - 5
apps/entropy-debugger/src/lib/utils.ts

@@ -1,5 +0,0 @@
-import type { ClassValue } from "clsx";
-import { clsx } from "clsx";
-import { twMerge } from "tailwind-merge";
-
-export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs));

+ 0 - 350
apps/entropy-debugger/src/store/entropy-deployments.ts

@@ -1,350 +0,0 @@
-export type EntropyDeployment = {
-  address: string;
-  network: "mainnet" | "testnet";
-  explorer: string;
-  delay: string;
-  gasLimit: string;
-  rpc?: string;
-  nativeCurrency: string;
-};
-
-export const EntropyDeployments = {
-  berachain: {
-    address: "0x36825bf3Fbdf5a29E2d5148bfe7Dcf7B5639e320",
-    network: "mainnet",
-    explorer: "https://berascan.com/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "2.5M",
-    rpc: "https://rpc.berachain.com",
-    nativeCurrency: "BERA",
-  },
-  blast: {
-    address: "0x5744Cbf430D99456a0A8771208b674F27f8EF0Fb",
-    network: "mainnet",
-    explorer: "https://blastscan.io/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "500K",
-    rpc: "https://rpc.blast.io",
-    nativeCurrency: "ETH",
-  },
-  arbitrum: {
-    address: "0x7698E925FfC29655576D0b361D75Af579e20AdAc",
-    network: "mainnet",
-    explorer: "https://arbiscan.io/address/$ADDRESS",
-    delay: "6 blocks",
-    gasLimit: "2.5M",
-    rpc: "https://arb1.arbitrum.io/rpc",
-    nativeCurrency: "ETH",
-  },
-  optimism: {
-    address: "0xdF21D137Aadc95588205586636710ca2890538d5",
-    network: "mainnet",
-    explorer: "https://optimistic.etherscan.io/address/$ADDRESS",
-    delay: "2 blocks",
-    gasLimit: "500K",
-    rpc: "https://optimism.drpc.org",
-    nativeCurrency: "ETH",
-  },
-  base: {
-    address: "0x6E7D74FA7d5c90FEF9F0512987605a6d546181Bb",
-    network: "mainnet",
-    explorer: "https://basescan.org/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "500K",
-    rpc: "https://developer-access-mainnet.base.org/",
-    nativeCurrency: "ETH",
-  },
-  "sei-evm-testnet": {
-    rpc: "https://evm-rpc-testnet.sei-apis.com",
-    network: "testnet",
-    delay: "1 block",
-    address: "0x36825bf3Fbdf5a29E2d5148bfe7Dcf7B5639e320",
-    explorer: "https://seitrace.com/address/$ADDRESS?chain=atlantic-2",
-    gasLimit: "500K",
-    nativeCurrency: "SEI",
-  },
-  "arbitrum-sepolia": {
-    rpc: "https://sepolia-rollup.arbitrum.io/rpc",
-    network: "testnet",
-    delay: "6 blocks",
-    address: "0x549Ebba8036Ab746611B4fFA1423eb0A4Df61440",
-    explorer: "https://sepolia.arbiscan.io/address/$ADDRESS",
-    gasLimit: "2.5M",
-    nativeCurrency: "ETH",
-  },
-  "blast-testnet": {
-    rpc: "https://sepolia.blast.io",
-    network: "testnet",
-    delay: "1 block",
-    address: "0x98046Bd286715D3B0BC227Dd7a956b83D8978603",
-    explorer: "https://testnet.blastscan.io/address/$ADDRESS",
-    gasLimit: "500K",
-    nativeCurrency: "ETH",
-  },
-  "optimism-sepolia": {
-    rpc: "https://sepolia.optimism.io",
-    network: "testnet",
-    delay: "2 blocks",
-    address: "0x4821932D0CDd71225A6d914706A621e0389D7061",
-    explorer: "https://optimism-sepolia.blockscout.com/address/$ADDRESS",
-    gasLimit: "500K",
-    nativeCurrency: "ETH",
-  },
-  "base-sepolia": {
-    rpc: "https://sepolia.base.org",
-    network: "testnet",
-    delay: "1 block",
-    address: "0x41c9e39574F40Ad34c79f1C99B66A45eFB830d4c",
-    explorer: "https://base-sepolia.blockscout.com/address/$ADDRESS",
-    gasLimit: "500K",
-    nativeCurrency: "ETH",
-  },
-  "sei-evm": {
-    address: "0x98046Bd286715D3B0BC227Dd7a956b83D8978603",
-    explorer: "https://seitrace.com/address/$ADDRESS?chain=pacific-1",
-    delay: "1 block",
-    gasLimit: "500K",
-    network: "mainnet",
-    rpc: "https://evm-rpc.sei-apis.com",
-    nativeCurrency: "SEI",
-  },
-  "etherlink-testnet": {
-    address: "0x23f0e8FAeE7bbb405E7A7C3d60138FCfd43d7509",
-    explorer: "https://testnet.explorer.etherlink.com/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "15M",
-    network: "testnet",
-    rpc: "https://node.ghostnet.etherlink.com",
-    nativeCurrency: "XTZ",
-  },
-  etherlink: {
-    address: "0x23f0e8FAeE7bbb405E7A7C3d60138FCfd43d7509",
-    explorer: "https://explorer.etherlink.com/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "15M",
-    network: "mainnet",
-    rpc: "https://node.mainnet.etherlink.com/",
-    nativeCurrency: "XTZ",
-  },
-  kaia: {
-    address: "0x36825bf3Fbdf5a29E2d5148bfe7Dcf7B5639e320",
-    explorer: "https://kaiascan.io/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "500K",
-    network: "mainnet",
-    rpc: "https://rpc.ankr.com/klaytn",
-    nativeCurrency: "KLAY",
-  },
-  "kaia-testnet": {
-    address: "0x36825bf3Fbdf5a29E2d5148bfe7Dcf7B5639e320",
-    explorer: "https://kairos.kaiascan.io/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "500K",
-    network: "testnet",
-    rpc: "https://rpc.ankr.com/klaytn_testnet",
-    nativeCurrency: "KLAY",
-  },
-  "tabi-testnet": {
-    address: "0xEbe57e8045F2F230872523bbff7374986E45C486",
-    explorer: "https://testnetv2.tabiscan.com/address/$ADDRESS",
-    delay: "0 block",
-    gasLimit: "500K",
-    network: "testnet",
-    rpc: "https://rpc.testnetv2.tabichain.com",
-    nativeCurrency: "TABI",
-  },
-  "b3-testnet": {
-    address: "0x5744Cbf430D99456a0A8771208b674F27f8EF0Fb",
-    explorer: "https://sepolia.explorer.b3.fun/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "500K",
-    network: "testnet",
-    rpc: "https://sepolia.b3.fun/http/",
-    nativeCurrency: "ETH",
-  },
-  b3: {
-    address: "0x5744Cbf430D99456a0A8771208b674F27f8EF0Fb",
-    explorer: "https://explorer.b3.fun/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "500K",
-    network: "mainnet",
-    rpc: "https://mainnet-rpc.b3.fun/http",
-    nativeCurrency: "ETH",
-  },
-  "apechain-testnet": {
-    address: "0x23f0e8FAeE7bbb405E7A7C3d60138FCfd43d7509",
-    explorer: "https://curtis.explorer.caldera.xyz/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "500K",
-    network: "testnet",
-    rpc: "https://curtis.rpc.caldera.xyz/http",
-    nativeCurrency: "APE",
-  },
-  "soneium-minato-testnet": {
-    address: "0x23f0e8FAeE7bbb405E7A7C3d60138FCfd43d7509",
-    explorer: "https://explorer-testnet.soneium.org/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "500K",
-    network: "testnet",
-    rpc: "https://rpc.minato.soneium.org/",
-    nativeCurrency: "ETH",
-  },
-  sanko: {
-    address: "0x5744Cbf430D99456a0A8771208b674F27f8EF0Fb",
-    explorer: "https://explorer.sanko.xyz/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "500K",
-    network: "mainnet",
-    rpc: "https://mainnet.sanko.xyz",
-    nativeCurrency: "DMT",
-  },
-  "sanko-testnet": {
-    address: "0x5744Cbf430D99456a0A8771208b674F27f8EF0Fb",
-    explorer: "https://sanko-arb-sepolia.explorer.caldera.xyz/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "500K",
-    network: "testnet",
-    rpc: "https://sanko-arb-sepolia.rpc.caldera.xyz/http",
-    nativeCurrency: "DMT",
-  },
-  apechain: {
-    address: "0x36825bf3Fbdf5a29E2d5148bfe7Dcf7B5639e320",
-    explorer: "https://apechain.calderaexplorer.xyz/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "500K",
-    network: "mainnet",
-    rpc: "https://apechain.calderachain.xyz/http",
-    nativeCurrency: "APE",
-  },
-  "abstract-testnet": {
-    address: "0x858687fD592112f7046E394A3Bf10D0C11fF9e63",
-    explorer: "https://explorer.testnet.abs.xyz/address/$ADDRESS",
-    delay: "0 block",
-    gasLimit: "500K",
-    network: "testnet",
-    rpc: "https://api.testnet.abs.xyz",
-    nativeCurrency: "ETH",
-  },
-  "sonic-blaze-testnet": {
-    address: "0xebe57e8045f2f230872523bbff7374986e45c486",
-    explorer: "https://blaze.soniclabs.com/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "500K",
-    network: "testnet",
-    rpc: "https://rpc.blaze.soniclabs.com",
-    nativeCurrency: "S",
-  },
-  "unichain-sepolia": {
-    address: "0x8D254a21b3C86D32F7179855531CE99164721933",
-    explorer: "https://unichain-sepolia.blockscout.com/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "500K",
-    network: "testnet",
-    rpc: "https://sepolia.unichain.org",
-    nativeCurrency: "ETH",
-  },
-  sonic: {
-    address: "0x36825bf3fbdf5a29e2d5148bfe7dcf7b5639e320",
-    explorer: "https://sonicscan.org/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "500K",
-    network: "mainnet",
-    rpc: "https://rpc.soniclabs.com",
-    nativeCurrency: "S",
-  },
-  "story-testnet": {
-    address: "0x5744Cbf430D99456a0A8771208b674F27f8EF0Fb",
-    explorer: "https://aeneid.storyscan.xyz/address/$ADDRESS",
-    delay: "0 block",
-    gasLimit: "500K",
-    network: "testnet",
-    rpc: "https://aeneid.storyrpc.io",
-    nativeCurrency: "IP",
-  },
-  "monad-testnet": {
-    address: "0x36825bf3Fbdf5a29E2d5148bfe7Dcf7B5639e320",
-    explorer: "https://testnet.monadexplorer.com/address/$ADDRESS",
-    delay: "2 blocks",
-    gasLimit: "500K",
-    network: "testnet",
-    rpc: "https://testnet-rpc.monad.xyz",
-    nativeCurrency: "MON",
-  },
-  abstract: {
-    address: "0x5a4a369F4db5df2054994AF031b7b23949b98c0e",
-    explorer: "https://abscan.org/address/$ADDRESS",
-    delay: "0 block",
-    gasLimit: "500K",
-    network: "mainnet",
-    rpc: "https://api.mainnet.abs.xyz",
-    nativeCurrency: "ETH",
-  },
-  story: {
-    address: "0xdF21D137Aadc95588205586636710ca2890538d5",
-    explorer: "https://storyscan.xyz/address/$ADDRESS",
-    delay: "0 block",
-    gasLimit: "500K",
-    network: "mainnet",
-    rpc: "https://mainnet.storyrpc.io",
-    nativeCurrency: "IP",
-  },
-  "berachain-bepolia": {
-    address: "0x36825bf3Fbdf5a29E2d5148bfe7Dcf7B5639e320",
-    explorer: "https://bepolia.beratrail.io/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "500K",
-    network: "testnet",
-    rpc: "https://bepolia.rpc.berachain.com",
-    nativeCurrency: "BERA",
-  },
-  hyperevm: {
-    address: "0xfA25E653b44586dBbe27eE9d252192F0e4956683",
-    explorer: "https://hyperliquid.cloud.blockscout.com/address/$ADDRESS",
-    delay: "0 block",
-    gasLimit: "500K",
-    network: "mainnet",
-    rpc: "https://rpc.hyperliquid.xyz/evm",
-    nativeCurrency: "HYPE",
-  },
-  soneium: {
-    address: "0x0708325268dF9F66270F1401206434524814508b",
-    explorer: "https://soneium.blockscout.com/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "500K",
-    network: "mainnet",
-    rpc: "https://soneium.drpc.org",
-    nativeCurrency: "ETH",
-  },
-  unichain: {
-    address: "0x36825bf3Fbdf5a29E2d5148bfe7Dcf7B5639e320",
-    explorer: "https://unichain.blockscout.com/address/$ADDRESS",
-    delay: "1 block",
-    gasLimit: "500K",
-    network: "mainnet",
-    rpc: "https://mainnet.unichain.org",
-    nativeCurrency: "ETH",
-  },
-  zetachain: {
-    address: "0x36825bf3Fbdf5a29E2d5148bfe7Dcf7B5639e320",
-    network: "mainnet",
-    explorer: "https://zetachain.blockscout.com/address/$ADDRESS",
-    delay: "0 block",
-    gasLimit: "500K",
-    rpc: "https://zetachain-evm.blockpi.network/v1/rpc/public",
-    nativeCurrency: "ZETA",
-  },
-  "zetachain-testnet": {
-    rpc: "https://zetachain-athens-evm.blockpi.network/v1/rpc/public",
-    network: "testnet",
-    delay: "",
-    address: "0x4374e5a8b9C22271E9EB878A2AA31DE97DF15DAF",
-    explorer: "https://explorer.zetachain.com/address/$ADDRESS",
-    gasLimit: "500K",
-    nativeCurrency: "ZETA",
-  },
-} as const satisfies Record<string, EntropyDeployment>;
-
-export const isValidDeployment = (
-  name: string,
-): name is keyof typeof EntropyDeployments =>
-  Object.prototype.hasOwnProperty.call(EntropyDeployments, name);

+ 0 - 63
apps/entropy-debugger/tailwind.config.ts

@@ -1,63 +0,0 @@
-import type { Config } from "tailwindcss";
-import animate from "tailwindcss-animate";
-
-export default {
-  darkMode: ["class"],
-  content: [
-    "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
-    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
-    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
-  ],
-  theme: {
-    extend: {
-      colors: {
-        background: "hsl(var(--background))",
-        foreground: "hsl(var(--foreground))",
-        card: {
-          DEFAULT: "hsl(var(--card))",
-          foreground: "hsl(var(--card-foreground))",
-        },
-        popover: {
-          DEFAULT: "hsl(var(--popover))",
-          foreground: "hsl(var(--popover-foreground))",
-        },
-        primary: {
-          DEFAULT: "hsl(var(--primary))",
-          foreground: "hsl(var(--primary-foreground))",
-        },
-        secondary: {
-          DEFAULT: "hsl(var(--secondary))",
-          foreground: "hsl(var(--secondary-foreground))",
-        },
-        muted: {
-          DEFAULT: "hsl(var(--muted))",
-          foreground: "hsl(var(--muted-foreground))",
-        },
-        accent: {
-          DEFAULT: "hsl(var(--accent))",
-          foreground: "hsl(var(--accent-foreground))",
-        },
-        destructive: {
-          DEFAULT: "hsl(var(--destructive))",
-          foreground: "hsl(var(--destructive-foreground))",
-        },
-        border: "hsl(var(--border))",
-        input: "hsl(var(--input))",
-        ring: "hsl(var(--ring))",
-        chart: {
-          "1": "hsl(var(--chart-1))",
-          "2": "hsl(var(--chart-2))",
-          "3": "hsl(var(--chart-3))",
-          "4": "hsl(var(--chart-4))",
-          "5": "hsl(var(--chart-5))",
-        },
-      },
-      borderRadius: {
-        lg: "var(--radius)",
-        md: "calc(var(--radius) - 2px)",
-        sm: "calc(var(--radius) - 4px)",
-      },
-    },
-  },
-  plugins: [animate],
-} satisfies Config;

+ 0 - 5
apps/entropy-debugger/tsconfig.json

@@ -1,5 +0,0 @@
-{
-  "extends": "@cprussin/tsconfig/nextjs.json",
-  "include": ["svg.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
-  "exclude": ["node_modules"]
-}

+ 0 - 3
apps/entropy-debugger/vercel.json

@@ -1,3 +0,0 @@
-{
-  "$schema": "https://openapi.vercel.sh/vercel.json"
-}

+ 2 - 0
apps/entropy-explorer/.gitignore

@@ -1 +1,3 @@
 .env*.local
+
+dist/

+ 6 - 0
apps/entropy-explorer/.prettierignore

@@ -5,3 +5,9 @@ node_modules/
 .env*.local
 .env
 .DS_Store
+dist/
+lib/
+build/
+node_modules/
+package.json
+tsconfig*.json

+ 4 - 1
apps/entropy-explorer/jest.config.js

@@ -1 +1,4 @@
-export { nextjs as default } from "@cprussin/jest-config/next";
+import { defineJestConfigForNextJs } from "@pythnetwork/jest-config/define-next-config";
+("@pythnetwork/jest-config");
+
+export default defineJestConfigForNextJs();

+ 3 - 4
apps/entropy-explorer/package.json

@@ -4,7 +4,7 @@
   "private": true,
   "type": "module",
   "engines": {
-    "node": "22"
+    "node": ">=22.14.0"
   },
   "scripts": {
     "build:vercel": "next build",
@@ -33,7 +33,7 @@
   },
   "devDependencies": {
     "@cprussin/eslint-config": "catalog:",
-    "@cprussin/jest-config": "catalog:",
+    "@pythnetwork/jest-config": "workspace:",
     "@cprussin/prettier-config": "catalog:",
     "@cprussin/tsconfig": "catalog:",
     "@svgr/webpack": "catalog:",
@@ -50,7 +50,6 @@
     "sass": "catalog:",
     "stylelint": "catalog:",
     "stylelint-config-standard-scss": "catalog:",
-    "typescript": "catalog:",
     "vercel": "catalog:"
   }
-}
+}

+ 9 - 0
apps/entropy-explorer/tsconfig.build.json

@@ -0,0 +1,9 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "noEmit": false,
+    "incremental": false,
+    "declaration": true
+  },
+  "exclude": ["node_modules", "dist", "examples/", "**/__tests__/*"]
+}

+ 2 - 0
apps/entropy-tester/.gitignore

@@ -1 +1,3 @@
 dist
+
+dist/

+ 6 - 0
apps/entropy-tester/.prettierignore

@@ -0,0 +1,6 @@
+dist/
+lib/
+build/
+node_modules/
+package.json
+tsconfig*.json

+ 16 - 12
apps/entropy-tester/package.json

@@ -4,27 +4,29 @@
   "description": "Utility to test entropy provider callbacks",
   "private": true,
   "type": "module",
-  "main": "dist/index.js",
-  "types": "dist/index.d.ts",
+  "main": "./dist/index.cjs",
+  "types": "./dist/index.d.ts",
   "exports": {
-    "import": {
+    ".": {
       "types": "./dist/index.d.ts",
-      "default": "./dist/index.js"
-    }
+      "default": "./dist/index.cjs"
+    },
+    "./package.json": "./package.json"
   },
   "files": [
     "dist/**/*",
     "cli/**/*"
   ],
   "scripts": {
-    "build": "tsc --project tsconfig.build.json",
+    "build": "ts-duality --noEsm",
     "fix:format": "prettier --write .",
     "fix:lint": "eslint --fix .",
     "test:format": "prettier --check .",
     "test:lint": "eslint . --max-warnings 0",
     "test:types": "tsc",
-    "start:dev": "tsc --project tsconfig.build.json && node cli/run.js",
-    "start:prod": "node cli/run.js"
+    "start:dev": "node cli/run.js",
+    "start:prod": "node cli/run.js",
+    "clean": "rm -rf ./dist"
   },
   "repository": {
     "type": "git",
@@ -46,8 +48,7 @@
     "eslint": "catalog:",
     "pino-pretty": "^11.2.1",
     "prettier": "catalog:",
-    "ts-node": "catalog:",
-    "typescript": "catalog:"
+    "ts-node": "catalog:"
   },
   "dependencies": {
     "@pythnetwork/contract-manager": "workspace:*",
@@ -60,5 +61,8 @@
   },
   "keywords": [],
   "author": "",
-  "license": "Apache-2.0"
-}
+  "license": "Apache-2.0",
+  "engines": {
+    "node": ">=22.14.0"
+  }
+}

+ 1 - 1
apps/entropy-tester/src/index.ts

@@ -4,7 +4,7 @@ import type { PrivateKey } from "@pythnetwork/contract-manager/core/base";
 import { toPrivateKey } from "@pythnetwork/contract-manager/core/base";
 import { EvmChain } from "@pythnetwork/contract-manager/core/chains";
 import { EvmEntropyContract } from "@pythnetwork/contract-manager/core/contracts/evm";
-import { DefaultStore } from "@pythnetwork/contract-manager/node/store";
+import { DefaultStore } from "@pythnetwork/contract-manager/node/utils/store";
 import type { Logger } from "pino";
 import { pino } from "pino";
 import yargs from "yargs";

+ 12 - 3
apps/entropy-tester/tsconfig.build.json

@@ -2,6 +2,15 @@
   "extends": "./tsconfig.json",
   "compilerOptions": {
     "outDir": "./dist",
-    "noEmit": false
-  }
-}
+    "noEmit": false,
+    "incremental": false,
+    "declaration": true,
+    "isolatedModules": false
+  },
+  "exclude": [
+    "node_modules",
+    "dist",
+    "examples/",
+    "**/__tests__/*"
+  ]
+}

+ 2 - 0
apps/fortuna/.gitignore

@@ -5,3 +5,5 @@
 .envrc
 fortuna.db*
 .env*
+
+dist/

+ 0 - 6
apps/hermes/client/js/.eslintrc.js

@@ -1,6 +0,0 @@
-module.exports = {
-  root: true,
-  parser: "@typescript-eslint/parser",
-  plugins: ["@typescript-eslint"],
-  extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
-};

+ 2 - 0
apps/hermes/client/js/.gitignore

@@ -1,3 +1,5 @@
 lib/
 src/zodSchemas.ts
 schema.json
+
+dist/

+ 7 - 0
apps/hermes/client/js/.prettierignore

@@ -0,0 +1,7 @@
+dist/
+lib/
+build/
+node_modules/
+src/zodSchemas.ts
+package.json
+tsconfig*.json

+ 12 - 0
apps/hermes/client/js/eslint.config.js

@@ -0,0 +1,12 @@
+import { base } from "@cprussin/eslint-config";
+import { globalIgnores } from "eslint/config";
+
+export default [
+  globalIgnores(["src/examples/**/*", "src/zodSchemas.ts"]),
+  ...base,
+  {
+    rules: {
+      "unicorn/filename-case": "off",
+    },
+  },
+];

+ 3 - 5
apps/hermes/client/js/jest.config.js

@@ -1,5 +1,3 @@
-/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
-module.exports = {
-  preset: "ts-jest",
-  testEnvironment: "node",
-};
+import { defineJestConfig } from "@pythnetwork/jest-config/define-config";
+
+export default defineJestConfig();

+ 60 - 13
apps/hermes/client/js/package.json

@@ -6,10 +6,8 @@
     "name": "Pyth Data Association"
   },
   "homepage": "https://pyth.network",
-  "main": "lib/HermesClient.js",
-  "types": "lib/HermesClient.d.ts",
   "files": [
-    "lib/**/*"
+    "dist/**/*"
   ],
   "repository": {
     "type": "git",
@@ -20,7 +18,7 @@
     "access": "public"
   },
   "scripts": {
-    "build:typescript": "tsc",
+    "build": "ts-duality",
     "build:schemas": "openapi-zod-client ./schema.json --output src/zodSchemas.ts",
     "pull:schema": "curl -o schema.json -z schema.json https://hermes.pyth.network/docs/openapi.json",
     "example": "node lib/examples/HermesClient.js",
@@ -28,9 +26,10 @@
     "fix:format": "prettier --write \"src/**/*.ts\"",
     "test:lint": "eslint src/ --max-warnings 0",
     "test:format": "prettier --check \"src/**/*.ts\"",
-    "prepublishOnly": "pnpm run build:typescript && pnpm run test:lint",
+    "prepublishOnly": "pnpm run build && pnpm run test:lint",
     "preversion": "pnpm run test:lint",
-    "version": "pnpm run format && git add -A src"
+    "version": "pnpm run format && git add -A src",
+    "clean": "rm -rf ./dist"
   },
   "keywords": [
     "pyth",
@@ -38,22 +37,70 @@
   ],
   "license": "Apache-2.0",
   "devDependencies": {
+    "@cprussin/eslint-config": "catalog:",
+    "@pythnetwork/jest-config": "workspace:",
     "@types/jest": "^29.4.0",
     "@types/node": "^20.14.2",
     "@types/yargs": "^17.0.10",
-    "@typescript-eslint/eslint-plugin": "^5.21.0",
-    "@typescript-eslint/parser": "^5.21.0",
-    "eslint": "^8.14.0",
+    "eslint": "catalog:",
     "jest": "^29.4.0",
     "openapi-zod-client": "^1.18.1",
     "prettier": "catalog:",
-    "ts-jest": "^29.0.5",
-    "typescript": "catalog:",
     "yargs": "^17.4.1"
   },
   "dependencies": {
     "@zodios/core": "^10.9.6",
     "eventsource": "^3.0.5",
     "zod": "^3.23.8"
-  }
-}
+  },
+  "engines": {
+    "node": ">=22.14.0"
+  },
+  "type": "module",
+  "exports": {
+    "./hermes-client": {
+      "require": {
+        "types": "./dist/cjs/hermes-client.d.ts",
+        "default": "./dist/cjs/hermes-client.cjs"
+      },
+      "import": {
+        "types": "./dist/esm/hermes-client.d.ts",
+        "default": "./dist/esm/hermes-client.mjs"
+      }
+    },
+    ".": {
+      "require": {
+        "types": "./dist/cjs/index.d.ts",
+        "default": "./dist/cjs/index.cjs"
+      },
+      "import": {
+        "types": "./dist/esm/index.d.ts",
+        "default": "./dist/esm/index.mjs"
+      }
+    },
+    "./utils": {
+      "require": {
+        "types": "./dist/cjs/utils.d.ts",
+        "default": "./dist/cjs/utils.cjs"
+      },
+      "import": {
+        "types": "./dist/esm/utils.d.ts",
+        "default": "./dist/esm/utils.mjs"
+      }
+    },
+    "./zodSchemas": {
+      "require": {
+        "types": "./dist/cjs/zodSchemas.d.ts",
+        "default": "./dist/cjs/zodSchemas.cjs"
+      },
+      "import": {
+        "types": "./dist/esm/zodSchemas.d.ts",
+        "default": "./dist/esm/zodSchemas.mjs"
+      }
+    },
+    "./package.json": "./package.json"
+  },
+  "module": "./dist/esm/index.mjs",
+  "types": "./dist/cjs/index.d.ts",
+  "main": "./dist/cjs/index.cjs"
+}

+ 1 - 3
apps/hermes/client/js/src/examples/HermesClient.ts → apps/hermes/client/js/src/examples/hermes-client.ts

@@ -42,9 +42,7 @@ function extractBasicAuthorizationHeadersFromUrl(urlString: string): {
   const headers: HeadersInit = {};
 
   if (url.username && url.password) {
-    headers["Authorization"] = `Basic ${btoa(
-      `${url.username}:${url.password}`,
-    )}`;
+    headers.Authorization = `Basic ${btoa(`${url.username}:${url.password}`)}`;
     url.username = "";
     url.password = "";
   }

+ 34 - 23
apps/hermes/client/js/src/HermesClient.ts → apps/hermes/client/js/src/hermes-client.ts

@@ -1,7 +1,11 @@
+/* eslint-disable @typescript-eslint/require-await */
+/* eslint-disable @typescript-eslint/no-misused-spread */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
 import { EventSource } from "eventsource";
-import { schemas } from "./zodSchemas";
 import { z } from "zod";
-import { camelToSnakeCaseObject } from "./utils";
+
+import { camelToSnakeCaseObject } from "./utils.js";
+import { schemas } from "./zodSchemas.js";
 
 // Accessing schema objects
 export type AssetType = z.infer<typeof schemas.AssetType>;
@@ -48,8 +52,8 @@ export class HermesClient {
   /**
    * Constructs a new Connection.
    *
-   * @param endpoint endpoint URL to the price service. Example: https://website/example/
-   * @param config Optional HermesClientConfig for custom configurations.
+   * @param endpoint - endpoint URL to the price service. Example: https://website/example/
+   * @param config - Optional HermesClientConfig for custom configurations.
    */
   constructor(endpoint: string, config?: HermesClientConfig) {
     this.baseURL = endpoint;
@@ -72,12 +76,13 @@ export class HermesClient {
           ...(options?.signal ? [options.signal] : []),
           AbortSignal.timeout(this.timeout),
         ]),
+
         headers: { ...this.headers, ...options?.headers },
       });
       if (!response.ok) {
         const errorBody = await response.text();
         throw new Error(
-          `HTTP error! status: ${response.status}${
+          `HTTP error! status: ${response.status.toString()}${
             errorBody ? `, body: ${errorBody}` : ""
           }`,
         );
@@ -102,7 +107,7 @@ export class HermesClient {
    * This endpoint can be filtered by asset type and query string.
    * This will throw an error if there is a network problem or the price service returns a non-ok response.
    *
-   * @param options Optional parameters:
+   * @param options - Optional parameters:
    *        - query: String to filter the price feeds. If provided, the results will be filtered to all price feeds whose symbol contains the query string. Query string is case insensitive. Example: "bitcoin".
    *        - assetType: String to filter the price feeds by asset type. Possible values are "crypto", "equity", "fx", "metal", "rates", "crypto_redemption_rate". Filter string is case insensitive.
    *
@@ -117,6 +122,7 @@ export class HermesClient {
     fetchOptions?: RequestInit;
   } = {}): Promise<PriceFeedMetadata[]> {
     const url = this.buildURL("price_feeds");
+    // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
     if (options) {
       const transformedOptions = camelToSnakeCaseObject(options);
       this.appendUrlSearchParams(url, transformedOptions);
@@ -133,7 +139,7 @@ export class HermesClient {
    * This endpoint can be customized by specifying the encoding type and whether the results should also return the parsed publisher caps.
    * This will throw an error if there is a network problem or the price service returns a non-ok response.
    *
-   * @param options Optional parameters:
+   * @param options - Optional parameters:
    *        - encoding: Encoding type. If specified, return the publisher caps in the encoding specified by the encoding parameter. Default is hex.
    *        - parsed: Boolean to specify if the parsed publisher caps should be included in the response. Default is false.
    *
@@ -148,6 +154,7 @@ export class HermesClient {
     fetchOptions?: RequestInit;
   } = {}): Promise<PublisherCaps> {
     const url = this.buildURL("updates/publisher_stake_caps/latest");
+    // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
     if (options) {
       this.appendUrlSearchParams(url, options);
     }
@@ -163,8 +170,8 @@ export class HermesClient {
    * This endpoint can be customized by specifying the encoding type and whether the results should also return the parsed price update using the options object.
    * This will throw an error if there is a network problem or the price service returns a non-ok response.
    *
-   * @param ids Array of hex-encoded price feed IDs for which updates are requested.
-   * @param options Optional parameters:
+   * @param ids - Array of hex-encoded price feed IDs for which updates are requested.
+   * @param options - Optional parameters:
    *        - encoding: Encoding type. If specified, return the price update in the encoding specified by the encoding parameter. Default is hex.
    *        - parsed: Boolean to specify if the parsed price update should be included in the response. Default is false.
    *        - ignoreInvalidPriceIds: Boolean to specify if invalid price IDs should be ignored instead of returning an error. Default is false.
@@ -198,9 +205,9 @@ export class HermesClient {
    * This endpoint can be customized by specifying the encoding type and whether the results should also return the parsed price update.
    * This will throw an error if there is a network problem or the price service returns a non-ok response.
    *
-   * @param publishTime Unix timestamp in seconds.
-   * @param ids Array of hex-encoded price feed IDs for which updates are requested.
-   * @param options Optional parameters:
+   * @param publishTime - Unix timestamp in seconds.
+   * @param ids - Array of hex-encoded price feed IDs for which updates are requested.
+   * @param options - Optional parameters:
    *        - encoding: Encoding type. If specified, return the price update in the encoding specified by the encoding parameter. Default is hex.
    *        - parsed: Boolean to specify if the parsed price update should be included in the response. Default is false.
    *        - ignoreInvalidPriceIds: Boolean to specify if invalid price IDs should be ignored instead of returning an error. Default is false.
@@ -217,7 +224,7 @@ export class HermesClient {
     },
     fetchOptions?: RequestInit,
   ): Promise<PriceUpdate> {
-    const url = this.buildURL(`updates/price/${publishTime}`);
+    const url = this.buildURL(`updates/price/${publishTime.toString()}`);
     for (const id of ids) {
       url.searchParams.append("ids[]", id);
     }
@@ -237,8 +244,8 @@ export class HermesClient {
    * This will return an EventSource that can be used to listen to streaming updates.
    * If an invalid hex-encoded ID is passed, it will throw an error.
    *
-   * @param ids Array of hex-encoded price feed IDs for which streaming updates are requested.
-   * @param options Optional parameters:
+   * @param ids - Array of hex-encoded price feed IDs for which streaming updates are requested.
+   * @param options - Optional parameters:
    *        - encoding: Encoding type. If specified, updates are returned in the specified encoding. Default is hex.
    *        - parsed: Boolean to specify if the parsed price update should be included in the response. Default is false.
    *        - allowUnordered: Boolean to specify if unordered updates are allowed to be included in the stream. Default is false.
@@ -258,9 +265,9 @@ export class HermesClient {
     },
   ): Promise<EventSource> {
     const url = this.buildURL("updates/price/stream");
-    ids.forEach((id) => {
+    for (const id of ids) {
       url.searchParams.append("ids[]", id);
-    });
+    }
 
     if (options) {
       const transformedOptions = camelToSnakeCaseObject(options);
@@ -284,10 +291,10 @@ export class HermesClient {
    * This endpoint can be customized by specifying the encoding type and whether the results should also return the calculated TWAP using the options object.
    * This will throw an error if there is a network problem or the price service returns a non-ok response.
    *
-   * @param ids Array of hex-encoded price feed IDs for which updates are requested.
-   * @param window_seconds The time window in seconds over which to calculate the TWAP, ending at the current time.
+   * @param ids - Array of hex-encoded price feed IDs for which updates are requested.
+   * @param window_seconds - The time window in seconds over which to calculate the TWAP, ending at the current time.
    *  For example, a value of 300 would return the most recent 5 minute TWAP. Must be greater than 0 and less than or equal to 600 seconds (10 minutes).
-   * @param options Optional parameters:
+   * @param options - Optional parameters:
    *        - encoding: Encoding type. If specified, return the TWAP binary data in the encoding specified by the encoding parameter. Default is hex.
    *        - parsed: Boolean to specify if the calculated TWAP should be included in the response. Default is false.
    *        - ignoreInvalidPriceIds: Boolean to specify if invalid price IDs should be ignored instead of returning an error. Default is false.
@@ -304,7 +311,9 @@ export class HermesClient {
     },
     fetchOptions?: RequestInit,
   ): Promise<TwapsResponse> {
-    const url = this.buildURL(`updates/twap/${window_seconds}/latest`);
+    const url = this.buildURL(
+      `updates/twap/${window_seconds.toString()}/latest`,
+    );
     for (const id of ids) {
       url.searchParams.append("ids[]", id);
     }
@@ -325,15 +334,17 @@ export class HermesClient {
     url: URL,
     params: Record<string, string | boolean>,
   ) {
-    Object.entries(params).forEach(([key, value]) => {
+    for (const [key, value] of Object.entries(params)) {
+      // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
       if (value !== undefined) {
         url.searchParams.append(key, String(value));
       }
-    });
+    }
   }
 
   private buildURL(endpoint: string) {
     return new URL(
+      // eslint-disable-next-line unicorn/relative-url-style
       `./v2/${endpoint}`,
       // We ensure the `baseURL` ends with a `/` so that URL doesn't resolve the
       // path relative to the parent.

+ 1 - 0
apps/hermes/client/js/src/index.ts

@@ -0,0 +1 @@
+export * from "./hermes-client.js";

+ 7 - 4
apps/hermes/client/js/src/utils.ts

@@ -1,13 +1,16 @@
+/* eslint-disable @typescript-eslint/no-non-null-assertion */
 function camelToSnakeCase(str: string): string {
-  return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
+  return str.replaceAll(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
 }
 
 export function camelToSnakeCaseObject(
   obj: Record<string, string | boolean>,
 ): Record<string, string | boolean> {
   const result: Record<string, string | boolean> = {};
-  Object.keys(obj).forEach((key) => {
-    result[camelToSnakeCase(key)] = obj[key];
-  });
+  for (const key of Object.keys(obj)) {
+    const newKey = camelToSnakeCase(key);
+    const val = obj[key];
+    result[newKey] = val!;
+  }
   return result;
 }

+ 15 - 0
apps/hermes/client/js/tsconfig.build.json

@@ -0,0 +1,15 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "noEmit": false,
+    "incremental": false,
+    "declaration": true,
+    "isolatedModules": false
+  },
+  "exclude": [
+    "node_modules",
+    "dist",
+    "src/examples/",
+    "**/__tests__/*"
+  ]
+}

+ 3 - 12
apps/hermes/client/js/tsconfig.json

@@ -1,17 +1,8 @@
 {
-  "extends": "../../../../tsconfig.base.json",
+  "extends": "@cprussin/tsconfig/base.json",
+  "include": ["src"],
   "compilerOptions": {
-    "target": "esnext",
-    "module": "commonjs",
-    "declaration": true,
-    "composite": true,
-    "declarationMap": true,
-    "incremental": true,
-    "outDir": "./lib",
-    "strict": true,
-    "rootDir": "src/",
-    "esModuleInterop": true
+    "lib": ["DOM", "ESNext"]
   },
-  "include": ["src"],
   "exclude": ["node_modules"]
 }

+ 2 - 0
apps/hermes/server/.gitignore

@@ -5,3 +5,5 @@
 src/network/p2p.pb.go
 src/network/p2p.proto
 tools/
+
+dist/

+ 2 - 0
apps/insights/.gitignore

@@ -1 +1,3 @@
 .env*.local
+
+dist/

+ 6 - 0
apps/insights/.prettierignore

@@ -5,3 +5,9 @@ node_modules/
 .env*.local
 .env
 .DS_Store
+dist/
+lib/
+build/
+node_modules/
+package.json
+tsconfig*.json

+ 4 - 1
apps/insights/jest.config.js

@@ -1 +1,4 @@
-export { nextjs as default } from "@cprussin/jest-config/next";
+import { defineJestConfigForNextJs } from "@pythnetwork/jest-config/define-next-config";
+("@pythnetwork/jest-config");
+
+export default defineJestConfigForNextJs();

+ 3 - 4
apps/insights/package.json

@@ -4,7 +4,7 @@
   "private": true,
   "type": "module",
   "engines": {
-    "node": "22"
+    "node": ">=22.14.0"
   },
   "scripts": {
     "build:vercel": "next build",
@@ -53,7 +53,7 @@
   },
   "devDependencies": {
     "@cprussin/eslint-config": "catalog:",
-    "@cprussin/jest-config": "catalog:",
+    "@pythnetwork/jest-config": "workspace:",
     "@cprussin/prettier-config": "catalog:",
     "@cprussin/tsconfig": "catalog:",
     "@pythnetwork/staking-sdk": "workspace:*",
@@ -71,7 +71,6 @@
     "sass": "catalog:",
     "stylelint": "catalog:",
     "stylelint-config-standard-scss": "catalog:",
-    "typescript": "catalog:",
     "vercel": "catalog:"
   }
-}
+}

+ 9 - 0
apps/insights/tsconfig.build.json

@@ -0,0 +1,9 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "noEmit": false,
+    "incremental": false,
+    "declaration": true
+  },
+  "exclude": ["node_modules", "dist", "examples/", "**/__tests__/*"]
+}

+ 0 - 10
apps/price_pusher/.eslintrc.js

@@ -1,10 +0,0 @@
-module.exports = {
-  root: true,
-  parser: "@typescript-eslint/parser",
-  plugins: ["@typescript-eslint"],
-  extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
-  rules: {
-    "@typescript-eslint/no-explicit-any": "off",
-    "@typescript-eslint/no-non-null-assertion": "off",
-  },
-};

+ 2 - 0
apps/price_pusher/.gitignore

@@ -2,3 +2,5 @@ docker-compose.yaml
 price-config.yaml
 lib
 mnemonic
+
+dist/

+ 6 - 0
apps/price_pusher/.prettierignore

@@ -0,0 +1,6 @@
+dist/
+lib/
+build/
+node_modules/
+package.json
+tsconfig*.json

+ 12 - 0
apps/price_pusher/eslint.config.js

@@ -0,0 +1,12 @@
+import { base } from "@cprussin/eslint-config";
+import { globalIgnores } from "eslint/config";
+
+export default [
+  globalIgnores(["src/__tests__/**/*", "src/examples/**/*", "**/*.json"]),
+  ...base,
+  {
+    rules: {
+      "unicorn/filename-case": "off",
+    },
+  },
+];

+ 3 - 5
apps/price_pusher/jest.config.js

@@ -1,5 +1,3 @@
-/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
-module.exports = {
-  preset: "ts-jest",
-  testEnvironment: "node",
-};
+import { defineJestConfig } from "@pythnetwork/jest-config/define-config";
+
+export default defineJestConfig();

+ 155 - 14
apps/price_pusher/package.json

@@ -3,13 +3,13 @@
   "version": "10.2.0",
   "description": "Pyth Price Pusher",
   "homepage": "https://pyth.network",
-  "main": "lib/index.js",
-  "types": "lib/index.d.ts",
+  "main": "./dist/index.cjs",
+  "types": "./dist/index.d.ts",
   "files": [
-    "lib/**/*"
+    "dist/**/*"
   ],
   "bin": {
-    "pyth-price-pusher": "./lib/index.js"
+    "pyth-price-pusher": "./dist/index.js"
   },
   "repository": {
     "type": "git",
@@ -20,16 +20,17 @@
     "access": "public"
   },
   "scripts": {
-    "build": "tsc",
+    "build": "ts-duality --noEsm",
     "fix:format": "prettier --write \"src/**/*.ts\"",
     "fix:lint": "eslint src/ --fix --max-warnings 0",
     "test:format": "prettier --check \"src/**/*.ts\"",
     "test:lint": "eslint src/ --max-warnings 0",
-    "start": "node lib/index.js",
+    "start": "node dist/index.js",
     "dev": "ts-node src/index.ts",
     "prepublishOnly": "pnpm run build && pnpm run test:lint",
     "preversion": "pnpm run test:lint",
-    "version": "pnpm run test:format && pnpm run test:lint && git add -A src"
+    "version": "pnpm run test:format && pnpm run test:lint && git add -A src",
+    "clean": "rm -rf ./dist"
   },
   "keywords": [
     "pyth",
@@ -44,19 +45,19 @@
   ],
   "license": "Apache-2.0",
   "devDependencies": {
+    "@cprussin/eslint-config": "catalog:",
+    "@pythnetwork/jest-config": "workspace:",
     "@types/ethereum-protocol": "^1.0.2",
     "@types/express": "^4.17.21",
     "@types/jest": "^27.4.1",
     "@types/yargs": "^17.0.10",
     "@typescript-eslint/eslint-plugin": "^6.0.0",
     "@typescript-eslint/parser": "^6.0.0",
-    "eslint": "^8.13.0",
+    "eslint": "catalog:",
     "jest": "^29.7.0",
     "pino-pretty": "^11.2.1",
     "prettier": "catalog:",
-    "ts-jest": "^29.1.1",
-    "ts-node": "catalog:",
-    "typescript": "catalog:"
+    "ts-node": "catalog:"
   },
   "dependencies": {
     "@aptos-labs/ts-sdk": "^1.39.0",
@@ -80,7 +81,7 @@
     "@types/pino": "^7.0.5",
     "aptos": "^1.8.5",
     "express": "^4.18.2",
-    "fuels": "^0.94.5",
+    "fuels": "catalog:",
     "jito-ts": "^3.0.1",
     "joi": "^17.6.0",
     "near-api-js": "^3.0.2",
@@ -89,5 +90,145 @@
     "viem": "^2.19.4",
     "yaml": "^2.1.1",
     "yargs": "^17.5.1"
-  }
-}
+  },
+  "engines": {
+    "node": ">=22.14.0"
+  },
+  "type": "module",
+  "exports": {
+    "./aptos/aptos": {
+      "types": "./dist/aptos/aptos.d.ts",
+      "default": "./dist/aptos/aptos.cjs"
+    },
+    "./aptos/balance-tracker": {
+      "types": "./dist/aptos/balance-tracker.d.ts",
+      "default": "./dist/aptos/balance-tracker.cjs"
+    },
+    "./aptos/command": {
+      "types": "./dist/aptos/command.d.ts",
+      "default": "./dist/aptos/command.cjs"
+    },
+    "./common": {
+      "types": "./dist/common.d.ts",
+      "default": "./dist/common.cjs"
+    },
+    "./controller": {
+      "types": "./dist/controller.d.ts",
+      "default": "./dist/controller.cjs"
+    },
+    "./evm/balance-tracker": {
+      "types": "./dist/evm/balance-tracker.d.ts",
+      "default": "./dist/evm/balance-tracker.cjs"
+    },
+    "./evm/command": {
+      "types": "./dist/evm/command.d.ts",
+      "default": "./dist/evm/command.cjs"
+    },
+    "./evm/custom-gas-station": {
+      "types": "./dist/evm/custom-gas-station.d.ts",
+      "default": "./dist/evm/custom-gas-station.cjs"
+    },
+    "./evm/evm": {
+      "types": "./dist/evm/evm.d.ts",
+      "default": "./dist/evm/evm.cjs"
+    },
+    "./evm/pyth-abi": {
+      "types": "./dist/evm/pyth-abi.d.ts",
+      "default": "./dist/evm/pyth-abi.cjs"
+    },
+    "./evm/pyth-contract": {
+      "types": "./dist/evm/pyth-contract.d.ts",
+      "default": "./dist/evm/pyth-contract.cjs"
+    },
+    "./evm/super-wallet": {
+      "types": "./dist/evm/super-wallet.d.ts",
+      "default": "./dist/evm/super-wallet.cjs"
+    },
+    "./fuel/command": {
+      "types": "./dist/fuel/command.d.ts",
+      "default": "./dist/fuel/command.cjs"
+    },
+    "./fuel/fuel": {
+      "types": "./dist/fuel/fuel.d.ts",
+      "default": "./dist/fuel/fuel.cjs"
+    },
+    ".": {
+      "types": "./dist/index.d.ts",
+      "default": "./dist/index.cjs"
+    },
+    "./injective/command": {
+      "types": "./dist/injective/command.d.ts",
+      "default": "./dist/injective/command.cjs"
+    },
+    "./injective/injective": {
+      "types": "./dist/injective/injective.d.ts",
+      "default": "./dist/injective/injective.cjs"
+    },
+    "./interface": {
+      "types": "./dist/interface.d.ts",
+      "default": "./dist/interface.cjs"
+    },
+    "./metrics": {
+      "types": "./dist/metrics.d.ts",
+      "default": "./dist/metrics.cjs"
+    },
+    "./near/command": {
+      "types": "./dist/near/command.d.ts",
+      "default": "./dist/near/command.cjs"
+    },
+    "./near/near": {
+      "types": "./dist/near/near.d.ts",
+      "default": "./dist/near/near.cjs"
+    },
+    "./options": {
+      "types": "./dist/options.d.ts",
+      "default": "./dist/options.cjs"
+    },
+    "./price-config": {
+      "types": "./dist/price-config.d.ts",
+      "default": "./dist/price-config.cjs"
+    },
+    "./pyth-price-listener": {
+      "types": "./dist/pyth-price-listener.d.ts",
+      "default": "./dist/pyth-price-listener.cjs"
+    },
+    "./solana/balance-tracker": {
+      "types": "./dist/solana/balance-tracker.d.ts",
+      "default": "./dist/solana/balance-tracker.cjs"
+    },
+    "./solana/command": {
+      "types": "./dist/solana/command.d.ts",
+      "default": "./dist/solana/command.cjs"
+    },
+    "./solana/solana": {
+      "types": "./dist/solana/solana.d.ts",
+      "default": "./dist/solana/solana.cjs"
+    },
+    "./sui/balance-tracker": {
+      "types": "./dist/sui/balance-tracker.d.ts",
+      "default": "./dist/sui/balance-tracker.cjs"
+    },
+    "./sui/command": {
+      "types": "./dist/sui/command.d.ts",
+      "default": "./dist/sui/command.cjs"
+    },
+    "./sui/sui": {
+      "types": "./dist/sui/sui.d.ts",
+      "default": "./dist/sui/sui.cjs"
+    },
+    "./ton/command": {
+      "types": "./dist/ton/command.d.ts",
+      "default": "./dist/ton/command.cjs"
+    },
+    "./ton/ton": {
+      "types": "./dist/ton/ton.d.ts",
+      "default": "./dist/ton/ton.cjs"
+    },
+    "./utils": {
+      "types": "./dist/utils.d.ts",
+      "default": "./dist/utils.cjs"
+    },
+    "./package.json": "./package.json"
+  },
+  "module": "./dist/esm/index.js"
+}

+ 37 - 35
apps/price_pusher/src/aptos/aptos.ts

@@ -1,13 +1,16 @@
-import {
-  ChainPriceListener,
-  IPricePusher,
-  PriceInfo,
-  PriceItem,
-} from "../interface";
-import { AptosAccount, AptosClient } from "aptos";
-import { DurationInSeconds } from "../utils";
+/* eslint-disable @typescript-eslint/restrict-template-expressions */
+/* eslint-disable unicorn/no-await-expression-member */
+/* eslint-disable @typescript-eslint/no-unsafe-argument */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable @typescript-eslint/no-unsafe-member-access */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
 import { HermesClient } from "@pythnetwork/hermes-client";
-import { Logger } from "pino";
+import { AptosAccount, AptosClient } from "aptos";
+import type { Logger } from "pino";
+
+import type { IPricePusher, PriceInfo, PriceItem } from "../interface.js";
+import { ChainPriceListener } from "../interface.js";
+import type { DurationInSeconds } from "../utils.js";
 
 export class AptosPriceListener extends ChainPriceListener {
   constructor(
@@ -49,9 +52,9 @@ export class AptosPriceListener extends ChainPriceListener {
         multiplier * Number(priceItemRes.price_feed.price.price.magnitude);
 
       this.logger.debug(
-        `Polled an Aptos on-chain price for feed ${this.priceIdToAlias.get(
-          priceId,
-        )} (${priceId}).`,
+        `Polled an Aptos on-chain price for feed ${
+          this.priceIdToAlias.get(priceId) ?? ""
+        } (${priceId}).`,
       );
 
       return {
@@ -59,9 +62,9 @@ export class AptosPriceListener extends ChainPriceListener {
         conf: priceItemRes.price_feed.price.conf,
         publishTime: Number(priceItemRes.price_feed.price.timestamp),
       };
-    } catch (err) {
+    } catch (error) {
       this.logger.error(
-        err,
+        error,
         `Polling Aptos on-chain price for ${priceId} failed.`,
       );
       return undefined;
@@ -94,6 +97,7 @@ export class AptosPricePusher implements IPricePusher {
     private pythContractAddress: string,
     private endpoint: string,
     private mnemonic: string,
+    // @ts-expect-error - TODO: this class member is unused. remove this exception when it is
     private overrideGasPriceMultiplier: number,
   ) {
     this.sequenceNumberLocked = false;
@@ -103,7 +107,7 @@ export class AptosPricePusher implements IPricePusher {
    * Gets price update data which then can be submitted to the Pyth contract to update the prices.
    * This will throw an axios error if there is a network problem or the price service returns a non-ok response (e.g: Invalid price ids)
    *
-   * @param priceIds Array of hex-encoded price ids.
+   * @param priceIds - Array of hex-encoded price ids.
    * @returns Array of price update data.
    */
   async getPriceFeedsUpdateData(priceIds: string[]): Promise<number[][]> {
@@ -111,9 +115,7 @@ export class AptosPricePusher implements IPricePusher {
       encoding: "base64",
       ignoreInvalidPriceIds: true,
     });
-    return response.binary.data.map((data) =>
-      Array.from(Buffer.from(data, "base64")),
-    );
+    return response.binary.data.map((data) => [...Buffer.from(data, "base64")]);
   }
 
   async updatePriceFeed(
@@ -131,8 +133,8 @@ export class AptosPricePusher implements IPricePusher {
     try {
       // get the latest VAAs for updatePriceFeed and then push them
       priceFeedUpdateData = await this.getPriceFeedsUpdateData(priceIds);
-    } catch (err) {
-      this.logger.error(err, "Error fetching the latest vaas to push.");
+    } catch (error) {
+      this.logger.error(error, "Error fetching the latest vaas to push.");
       return;
     }
 
@@ -151,7 +153,7 @@ export class AptosPricePusher implements IPricePusher {
         arguments: [priceFeedUpdateData],
       },
       {
-        sequence_number: sequenceNumber.toFixed(),
+        sequence_number: sequenceNumber.toFixed(0),
       },
     );
 
@@ -168,11 +170,11 @@ export class AptosPricePusher implements IPricePusher {
       // to go out of sync. Missing transactions are rare and we don't want this check to block
       // the next price update. So we use spawn a promise without awaiting on it to wait for the
       // transaction to be confirmed and if it fails, it resets the sequence number and return.
-      this.waitForTransactionConfirmation(client, pendingTx.hash);
+      void this.waitForTransactionConfirmation(client, pendingTx.hash);
 
       return;
-    } catch (err: any) {
-      this.logger.error(err, "Error executing messages");
+    } catch (error: any) {
+      this.logger.error(error, "Error executing messages");
 
       // Reset the sequence number to re-sync it (in case that was the issue)
       this.lastSequenceNumber = undefined;
@@ -193,9 +195,9 @@ export class AptosPricePusher implements IPricePusher {
       });
 
       this.logger.info({ hash: txHash }, `Transaction confirmed.`);
-    } catch (err) {
+    } catch (error) {
       this.logger.error(
-        { err, hash: txHash },
+        { err: error, hash: txHash },
         `Transaction failed to confirm.`,
       );
 
@@ -210,14 +212,13 @@ export class AptosPricePusher implements IPricePusher {
     client: AptosClient,
     account: AptosAccount,
   ): Promise<number> {
-    if (this.lastSequenceNumber !== undefined) {
-      this.lastSequenceNumber += 1;
-      return this.lastSequenceNumber;
-    } else {
+    if (this.lastSequenceNumber === undefined) {
       // Fetch from the blockchain if we don't have the local cache.
       // Note that this is locked so that only 1 fetch occurs regardless of how many updates
       // happen during that fetch.
-      if (!this.sequenceNumberLocked) {
+      if (this.sequenceNumberLocked) {
+        throw new Error("Waiting for sequence number in another thread.");
+      } else {
         try {
           this.sequenceNumberLocked = true;
           this.lastSequenceNumber = Number(
@@ -227,14 +228,15 @@ export class AptosPricePusher implements IPricePusher {
             `Fetched account sequence number: ${this.lastSequenceNumber}`,
           );
           return this.lastSequenceNumber;
-        } catch (e: any) {
-          throw new Error("Failed to retrieve sequence number" + e);
+        } catch (error: any) {
+          throw new Error(`Failed to retrieve sequence number ${error}`);
         } finally {
           this.sequenceNumberLocked = false;
         }
-      } else {
-        throw new Error("Waiting for sequence number in another thread.");
       }
+    } else {
+      this.lastSequenceNumber += 1;
+      return this.lastSequenceNumber;
     }
   }
 }

+ 12 - 11
apps/price_pusher/src/aptos/balance-tracker.ts

@@ -1,24 +1,25 @@
 import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk";
-import {
-  BaseBalanceTracker,
+import type { Logger } from "pino";
+
+import type {
   BaseBalanceTrackerConfig,
   IBalanceTracker,
-} from "../interface";
-import { DurationInSeconds } from "../utils";
-import { PricePusherMetrics } from "../metrics";
-import { Logger } from "pino";
+} from "../interface.js";
+import { BaseBalanceTracker } from "../interface.js";
+import { PricePusherMetrics } from "../metrics.js";
+import type { DurationInSeconds } from "../utils.js";
 
 /**
  * Aptos-specific configuration for balance tracker
  */
-export interface AptosBalanceTrackerConfig extends BaseBalanceTrackerConfig {
+export type AptosBalanceTrackerConfig = {
   /** Aptos node endpoint URL */
   endpoint: string;
   /** Aptos account address */
   address: string;
   /** Optional decimal places for APT token (default: 8) */
   decimals?: number;
-}
+} & BaseBalanceTrackerConfig;
 
 /**
  * Aptos-specific implementation of the balance tracker
@@ -80,7 +81,7 @@ export class AptosBalanceTracker extends BaseBalanceTracker {
 /**
  * Parameters for creating an Aptos balance tracker
  */
-export interface CreateAptosBalanceTrackerParams {
+export type CreateAptosBalanceTrackerParams = {
   endpoint: string;
   address: string;
   network: string;
@@ -88,7 +89,7 @@ export interface CreateAptosBalanceTrackerParams {
   metrics: PricePusherMetrics;
   logger: Logger;
   decimals?: number;
-}
+};
 
 /**
  * Factory function to create a balance tracker for Aptos chain
@@ -104,5 +105,5 @@ export function createAptosBalanceTracker(
     metrics: params.metrics,
     logger: params.logger,
     decimals: params.decimals,
-  });
+  } as AptosBalanceTrackerConfig);
 }

+ 22 - 15
apps/price_pusher/src/aptos/command.ts

@@ -1,20 +1,27 @@
+/* eslint-disable @typescript-eslint/no-non-null-assertion */
+/* eslint-disable @typescript-eslint/restrict-template-expressions */
+/* eslint-disable @typescript-eslint/no-unsafe-argument */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import fs from "node:fs";
+
 import { HermesClient } from "@pythnetwork/hermes-client";
-import * as options from "../options";
-import { readPriceConfigFile } from "../price-config";
-import fs from "fs";
-import { PythPriceListener } from "../pyth-price-listener";
-import { Controller } from "../controller";
-import { Options } from "yargs";
+import { AptosAccount } from "aptos";
+import pino from "pino";
+import type { Options } from "yargs";
+
+import { Controller } from "../controller.js";
+import { PricePusherMetrics } from "../metrics.js";
+import * as options from "../options.js";
+import { readPriceConfigFile } from "../price-config.js";
+import { PythPriceListener } from "../pyth-price-listener.js";
 import {
   AptosPriceListener,
   AptosPricePusher,
   APTOS_ACCOUNT_HD_PATH,
-} from "./aptos";
-import { AptosAccount } from "aptos";
-import pino from "pino";
-import { filterInvalidPriceItems } from "../utils";
-import { PricePusherMetrics } from "../metrics";
-import { createAptosBalanceTracker } from "./balance-tracker";
+} from "./aptos.js";
+import { filterInvalidPriceItems } from "../utils.js";
+import { createAptosBalanceTracker } from "./balance-tracker.js";
 
 export default {
   command: "aptos",
@@ -76,7 +83,7 @@ export default {
       logger.info(`Metrics server started on port ${metricsPort}`);
     }
 
-    const mnemonic = fs.readFileSync(mnemonicFile, "utf-8").trim();
+    const mnemonic = fs.readFileSync(mnemonicFile, "utf8").trim();
     const account = AptosAccount.fromDerivePath(
       APTOS_ACCOUNT_HD_PATH,
       mnemonic,
@@ -130,7 +137,7 @@ export default {
       logger.child({ module: "Controller" }, { level: controllerLogLevel }),
       {
         pushingFrequency,
-        metrics,
+        metrics: metrics!,
       },
     );
 
@@ -149,6 +156,6 @@ export default {
       await balanceTracker.start();
     }
 
-    controller.start();
+    void controller.start();
   },
 };

+ 11 - 8
apps/price_pusher/src/controller.ts

@@ -1,13 +1,16 @@
-import { UnixTimestamp } from "@pythnetwork/hermes-client";
-import { DurationInSeconds, sleep } from "./utils";
-import { IPriceListener, IPricePusher } from "./interface";
-import { PriceConfig, shouldUpdate, UpdateCondition } from "./price-config";
-import { Logger } from "pino";
-import { PricePusherMetrics } from "./metrics";
+import type { UnixTimestamp } from "@pythnetwork/hermes-client";
+import type { Logger } from "pino";
+
+import type { IPriceListener, IPricePusher } from "./interface.js";
+import { PricePusherMetrics } from "./metrics.js";
+import type { PriceConfig } from "./price-config.js";
+import { shouldUpdate, UpdateCondition } from "./price-config.js";
+import type { DurationInSeconds } from "./utils.js";
+import { sleep } from "./utils.js";
 
 export class Controller {
   private pushingFrequency: DurationInSeconds;
-  private metrics?: PricePusherMetrics;
+  private metrics?: PricePusherMetrics | undefined;
 
   constructor(
     private priceConfigs: PriceConfig[],
@@ -90,7 +93,7 @@ export class Controller {
           priceShouldUpdate == UpdateCondition.EARLY
         ) {
           pricesToPush.push(priceConfig);
-          pubTimesToPush.push((targetLatestPrice?.publishTime || 0) + 1);
+          pubTimesToPush.push((targetLatestPrice?.publishTime ?? 0) + 1);
         }
       }
       if (pushThresholdMet) {

+ 12 - 11
apps/price_pusher/src/evm/balance-tracker.ts

@@ -1,22 +1,23 @@
-import { SuperWalletClient } from "./super-wallet";
-import {
-  BaseBalanceTracker,
+import type { Logger } from "pino";
+
+import type { SuperWalletClient } from "./super-wallet.js";
+import type {
   BaseBalanceTrackerConfig,
   IBalanceTracker,
-} from "../interface";
-import { DurationInSeconds } from "../utils";
-import { PricePusherMetrics } from "../metrics";
-import { Logger } from "pino";
+} from "../interface.js";
+import { BaseBalanceTracker } from "../interface.js";
+import { PricePusherMetrics } from "../metrics.js";
+import type { DurationInSeconds } from "../utils.js";
 
 /**
  * EVM-specific configuration for balance tracker
  */
-export interface EvmBalanceTrackerConfig extends BaseBalanceTrackerConfig {
+export type EvmBalanceTrackerConfig = {
   /** EVM wallet client */
   client: SuperWalletClient;
   /** EVM address with 0x prefix */
   address: `0x${string}`;
-}
+} & BaseBalanceTrackerConfig;
 
 /**
  * EVM-specific implementation of the balance tracker
@@ -60,14 +61,14 @@ export class EvmBalanceTracker extends BaseBalanceTracker {
 /**
  * Parameters for creating an EVM balance tracker
  */
-export interface CreateEvmBalanceTrackerParams {
+export type CreateEvmBalanceTrackerParams = {
   client: SuperWalletClient;
   address: `0x${string}`;
   network: string;
   updateInterval: DurationInSeconds;
   metrics: PricePusherMetrics;
   logger: Logger;
-}
+};
 
 /**
  * Factory function to create a balance tracker for EVM chains

+ 23 - 16
apps/price_pusher/src/evm/command.ts

@@ -1,18 +1,25 @@
+/* eslint-disable @typescript-eslint/no-non-null-assertion */
+/* eslint-disable @typescript-eslint/restrict-template-expressions */
+/* eslint-disable @typescript-eslint/no-unsafe-argument */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import fs from "node:fs";
+
 import { HermesClient } from "@pythnetwork/hermes-client";
-import fs from "fs";
-import { Options } from "yargs";
-import * as options from "../options";
-import { readPriceConfigFile } from "../price-config";
-import { PythPriceListener } from "../pyth-price-listener";
-import { Controller } from "../controller";
-import { EvmPriceListener, EvmPricePusher } from "./evm";
-import { getCustomGasStation } from "./custom-gas-station";
 import pino from "pino";
-import { createClient } from "./super-wallet";
-import { createPythContract } from "./pyth-contract";
-import { isWsEndpoint, filterInvalidPriceItems } from "../utils";
-import { PricePusherMetrics } from "../metrics";
-import { createEvmBalanceTracker } from "./balance-tracker";
+import type { Options } from "yargs";
+
+import { Controller } from "../controller.js";
+import * as options from "../options.js";
+import { readPriceConfigFile } from "../price-config.js";
+import { PythPriceListener } from "../pyth-price-listener.js";
+import { getCustomGasStation } from "./custom-gas-station.js";
+import { EvmPriceListener, EvmPricePusher } from "./evm.js";
+import { createPythContract } from "./pyth-contract.js";
+import { createClient } from "./super-wallet.js";
+import { PricePusherMetrics } from "../metrics.js";
+import { isWsEndpoint, filterInvalidPriceItems } from "../utils.js";
+import { createEvmBalanceTracker } from "./balance-tracker.js";
 
 export default {
   command: "evm",
@@ -118,7 +125,7 @@ export default {
     const priceConfigs = readPriceConfigFile(priceConfigFile);
     const hermesClient = new HermesClient(priceServiceEndpoint);
 
-    const mnemonic = fs.readFileSync(mnemonicFile, "utf-8").trim();
+    const mnemonic = fs.readFileSync(mnemonicFile, "utf8").trim();
 
     let priceItems = priceConfigs.map(({ id, alias }) => ({ id, alias }));
 
@@ -198,7 +205,7 @@ export default {
       logger.child({ module: "Controller" }, { level: controllerLogLevel }),
       {
         pushingFrequency,
-        metrics,
+        metrics: metrics!,
       },
     );
 
@@ -217,6 +224,6 @@ export default {
       await balanceTracker.start();
     }
 
-    await controller.start();
+    void controller.start();
   },
 };

+ 15 - 12
apps/price_pusher/src/evm/custom-gas-station.ts

@@ -1,13 +1,14 @@
-import {
-  CustomGasChainId,
-  TxSpeed,
-  verifyValidOption,
-  txSpeeds,
-  customGasChainIds,
-} from "../utils";
-import { Logger } from "pino";
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable @typescript-eslint/no-unsafe-call */
+/* eslint-disable @typescript-eslint/no-unsafe-argument */
+/* eslint-disable @typescript-eslint/no-unsafe-member-access */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+import type { Logger } from "pino";
 import { parseGwei } from "viem";
 
+import type { CustomGasChainId, TxSpeed } from "../utils.js";
+import { verifyValidOption, txSpeeds, customGasChainIds } from "../utils.js";
+
 type chainMethods = Record<CustomGasChainId, () => Promise<bigint | undefined>>;
 
 export class CustomGasStation {
@@ -30,15 +31,16 @@ export class CustomGasStation {
   private async fetchMaticMainnetGasPrice() {
     try {
       const res = await fetch("https://gasstation.polygon.technology/v2");
-      const jsonRes = await res.json();
+      // TODO: improve the typing specificity here
+      const jsonRes = (await res.json()) as any;
       const gasPrice = jsonRes[this.speed].maxFee;
       return parseGwei(gasPrice.toFixed(2));
-    } catch (err) {
+    } catch (error) {
       this.logger.error(
-        err,
+        error,
         "Failed to fetch gas price from Matic mainnet. Returning undefined",
       );
-      return undefined;
+      return;
     }
   }
 }
@@ -51,4 +53,5 @@ export function getCustomGasStation(
   if (customGasStation && txSpeed) {
     return new CustomGasStation(logger, customGasStation, txSpeed);
   }
+  return;
 }

+ 64 - 67
apps/price_pusher/src/evm/evm.ts

@@ -1,26 +1,13 @@
+/* eslint-disable @typescript-eslint/no-unsafe-member-access */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable @typescript-eslint/restrict-template-expressions */
+/* eslint-disable @typescript-eslint/require-await */
+import type { HexString, UnixTimestamp } from "@pythnetwork/hermes-client";
+import { HermesClient } from "@pythnetwork/hermes-client";
+import type { Logger } from "pino";
+import type { WatchContractEventOnLogsParameter } from "viem";
 import {
-  IPricePusher,
-  PriceInfo,
-  ChainPriceListener,
-  PriceItem,
-} from "../interface";
-import {
-  addLeading0x,
-  assertDefined,
-  DurationInSeconds,
-  removeLeading0x,
-} from "../utils";
-import { PythAbi } from "./pyth-abi";
-import { Logger } from "pino";
-import {
-  HermesClient,
-  HexString,
-  UnixTimestamp,
-} from "@pythnetwork/hermes-client";
-import { CustomGasStation } from "./custom-gas-station";
-import { PushAttempt } from "../common";
-import {
-  WatchContractEventOnLogsParameter,
   TransactionExecutionError,
   BaseError,
   ContractFunctionRevertedError,
@@ -30,8 +17,15 @@ import {
   ContractFunctionExecutionError,
 } from "viem";
 
-import { PythContract } from "./pyth-contract";
-import { SuperWalletClient } from "./super-wallet";
+import type { PushAttempt } from "../common.js";
+import type { IPricePusher, PriceInfo, PriceItem } from "../interface.js";
+import type { DurationInSeconds } from "../utils.js";
+import { CustomGasStation } from "./custom-gas-station";
+import type { PythContract } from "./pyth-contract.js";
+import { addLeading0x, assertDefined, removeLeading0x } from "../utils.js";
+import { PythAbi } from "./pyth-abi.js";
+import type { SuperWalletClient } from "./super-wallet.js";
+import { ChainPriceListener } from "../interface.js";
 
 export class EvmPriceListener extends ChainPriceListener {
   constructor(
@@ -51,10 +45,10 @@ export class EvmPriceListener extends ChainPriceListener {
 
   // This method should be awaited on and once it finishes it has the latest value
   // for the given price feeds (if they exist).
-  async start() {
+  override async start() {
     if (this.watchEvents) {
       this.logger.info("Watching target network pyth contract events...");
-      this.startWatching();
+      void this.startWatching();
     } else {
       this.logger.info(
         "The target network RPC endpoint is not Websocket. " +
@@ -104,8 +98,8 @@ export class EvmPriceListener extends ChainPriceListener {
       priceRaw = await this.pythContract.read.getPriceUnsafe([
         addLeading0x(priceId),
       ]);
-    } catch (err) {
-      this.logger.error(err, `Polling on-chain price for ${priceId} failed.`);
+    } catch (error) {
+      this.logger.error(error, `Polling on-chain price for ${priceId} failed.`);
       return undefined;
     }
 
@@ -175,12 +169,12 @@ export class EvmPricePusher implements IPricePusher {
         Math.round(Number(updateFee) * (this.updateFeeMultiplier || 1)),
       );
       this.logger.debug(`Update fee: ${updateFee}`);
-    } catch (e: any) {
+    } catch (error: any) {
       this.logger.error(
-        e,
+        error,
         "An unidentified error has occured when getting the update fee.",
       );
-      throw e;
+      throw error;
     }
 
     // Gas price in networks with transaction type eip1559 represents the
@@ -195,9 +189,7 @@ export class EvmPricePusher implements IPricePusher {
       );
 
     // Try to re-use the same nonce and increase the gas if the last tx is not landed yet.
-    if (this.pusherAddress === undefined) {
-      this.pusherAddress = this.client.account.address;
-    }
+    this.pusherAddress ??= this.client.account.address;
 
     const lastExecutedNonce =
       (await this.client.getTransactionCount({
@@ -229,9 +221,7 @@ export class EvmPricePusher implements IPricePusher {
 
     this.logger.debug(`Using gas price: ${gasPrice} and nonce: ${txNonce}`);
 
-    const pubTimesToPushParam = pubTimesToPush.map((pubTime) =>
-      BigInt(pubTime),
-    );
+    const pubTimesToPushParam = pubTimesToPush.map(BigInt);
 
     const priceIdsWith0x = priceIds.map((priceId) => addLeading0x(priceId));
 
@@ -250,9 +240,9 @@ export class EvmPricePusher implements IPricePusher {
             gasPrice: BigInt(Math.ceil(gasPrice)),
             nonce: txNonce,
             gas:
-              this.gasLimit !== undefined
-                ? BigInt(Math.ceil(this.gasLimit))
-                : undefined,
+              this.gasLimit === undefined
+                ? undefined
+                : BigInt(Math.ceil(this.gasLimit)),
           },
         );
 
@@ -262,13 +252,16 @@ export class EvmPricePusher implements IPricePusher {
 
       this.logger.info({ hash }, "Price update sent");
 
-      this.waitForTransactionReceipt(hash);
-    } catch (err: any) {
-      this.logger.debug({ err }, "Simulating or sending transactions failed.");
+      void this.waitForTransactionReceipt(hash);
+    } catch (error: any) {
+      this.logger.debug(
+        { err: error },
+        "Simulating or sending transactions failed.",
+      );
 
-      if (err instanceof BaseError) {
+      if (error instanceof BaseError) {
         if (
-          err.walk(
+          error.walk(
             (e) =>
               e instanceof ContractFunctionRevertedError &&
               e.data?.errorName === "NoFreshUpdate",
@@ -280,18 +273,18 @@ export class EvmPricePusher implements IPricePusher {
           return;
         }
 
-        if (err.walk((e) => e instanceof InsufficientFundsError)) {
+        if (error.walk((e) => e instanceof InsufficientFundsError)) {
           this.logger.error(
-            { err },
+            { err: error },
             "Wallet doesn't have enough balance. In rare cases, there might be issues with gas price " +
               "calculation in the RPC.",
           );
-          throw err;
+          throw error;
         }
 
         if (
-          err.walk((e) => e instanceof FeeCapTooLowError) ||
-          err.walk(
+          error.walk((e) => e instanceof FeeCapTooLowError) ||
+          error.walk(
             (e) =>
               e instanceof InternalRpcError &&
               e.details.includes("replacement transaction underpriced"),
@@ -307,7 +300,7 @@ export class EvmPricePusher implements IPricePusher {
         }
 
         if (
-          err.walk(
+          error.walk(
             (e) =>
               e instanceof TransactionExecutionError &&
               (e.details.includes("nonce too low") ||
@@ -321,9 +314,9 @@ export class EvmPricePusher implements IPricePusher {
         }
 
         // Sometimes the contract function execution fails in simulation and this error is thrown.
-        if (err.walk((e) => e instanceof ContractFunctionExecutionError)) {
+        if (error.walk((e) => e instanceof ContractFunctionExecutionError)) {
           this.logger.warn(
-            { err },
+            { err: error },
             "The contract function execution failed in simulation. This is an expected behaviour in high frequency or multi-instance setup. " +
               "Please review this error and file an issue if it is a bug. Skipping this push.",
           );
@@ -332,9 +325,9 @@ export class EvmPricePusher implements IPricePusher {
 
         // We normally crash on unknown failures but we believe that this type of error is safe to skip. The other reason is that
         // wometimes we see a TransactionExecutionError because of the nonce without any details and it is not catchable.
-        if (err.walk((e) => e instanceof TransactionExecutionError)) {
+        if (error.walk((e) => e instanceof TransactionExecutionError)) {
           this.logger.error(
-            { err },
+            { err: error },
             "Transaction execution failed. This is an expected behaviour in high frequency or multi-instance setup. " +
               "Please review this error and file an issue if it is a bug. Skipping this push.",
           );
@@ -344,9 +337,9 @@ export class EvmPricePusher implements IPricePusher {
         // The following errors are part of the legacy code and might not work as expected.
         // We are keeping them in case they help with handling what is not covered above.
         if (
-          err.message.includes("the tx doesn't have the correct nonce.") ||
-          err.message.includes("nonce too low") ||
-          err.message.includes("invalid nonce")
+          error.message.includes("the tx doesn't have the correct nonce.") ||
+          error.message.includes("nonce too low") ||
+          error.message.includes("invalid nonce")
         ) {
           this.logger.info(
             "The nonce is incorrect (are multiple users using this account?). Skipping this push.",
@@ -354,7 +347,9 @@ export class EvmPricePusher implements IPricePusher {
           return;
         }
 
-        if (err.message.includes("max fee per gas less than block base fee")) {
+        if (
+          error.message.includes("max fee per gas less than block base fee")
+        ) {
           // We just have to handle this error and return.
           // LastPushAttempt was stored with the class
           // Next time the update will be executing, it will check the last attempt
@@ -367,13 +362,13 @@ export class EvmPricePusher implements IPricePusher {
         }
 
         if (
-          err.message.includes("sender doesn't have enough funds to send tx.")
+          error.message.includes("sender doesn't have enough funds to send tx.")
         ) {
           this.logger.error("Payer is out of balance, please top it up.");
           throw new Error("Please top up the wallet");
         }
 
-        if (err.message.includes("could not replace existing tx")) {
+        if (error.message.includes("could not replace existing tx")) {
           this.logger.error(
             "A transaction with the same nonce has been mined and this one is no longer needed. Skipping this push.",
           );
@@ -383,11 +378,11 @@ export class EvmPricePusher implements IPricePusher {
 
       // If the error is not handled, we will crash the process.
       this.logger.error(
-        { err },
+        { err: error },
         "The transaction failed with an unhandled error. crashing the process. " +
           "Please review this error and file an issue if it is a bug.",
       );
-      throw err;
+      throw error;
     }
   }
 
@@ -398,19 +393,21 @@ export class EvmPricePusher implements IPricePusher {
       });
 
       switch (receipt.status) {
-        case "success":
+        case "success": {
           this.logger.debug({ hash, receipt }, "Price update successful");
           this.logger.info({ hash }, "Price update successful");
           break;
-        default:
+        }
+        default: {
           this.logger.info(
             { hash, receipt },
             "Price update did not succeed or its transaction did not land. " +
               "This is an expected behaviour in high frequency or multi-instance setup.",
           );
+        }
       }
-    } catch (err: any) {
-      this.logger.warn({ err }, "Failed to get transaction receipt");
+    } catch (error: any) {
+      this.logger.warn({ err: error }, "Failed to get transaction receipt");
     }
   }
 

+ 5 - 3
apps/price_pusher/src/evm/pyth-contract.ts

@@ -1,6 +1,8 @@
-import { getContract, Address, GetContractReturnType } from "viem";
-import { PythAbi } from "./pyth-abi";
-import { SuperWalletClient } from "./super-wallet";
+import type { Address, GetContractReturnType } from "viem";
+import { getContract } from "viem";
+
+import { PythAbi } from "./pyth-abi.js";
+import type { SuperWalletClient } from "./super-wallet.js";
 
 export type PythContract = GetContractReturnType<
   typeof PythAbi,

+ 12 - 8
apps/price_pusher/src/evm/super-wallet.ts

@@ -1,12 +1,6 @@
-import {
-  createPublicClient,
-  createWalletClient,
-  defineChain,
-  http,
-  webSocket,
+import type {
   Account,
   Chain,
-  publicActions,
   Client,
   RpcSchema,
   WalletActions,
@@ -15,9 +9,18 @@ import {
   HttpTransport,
   Transport,
 } from "viem";
+import {
+  createPublicClient,
+  createWalletClient,
+  defineChain,
+  http,
+  webSocket,
+  publicActions,
+} from "viem";
 import { mnemonicToAccount } from "viem/accounts";
 import * as chains from "viem/chains";
-import { isWsEndpoint } from "../utils";
+
+import { isWsEndpoint } from "../utils.js";
 
 const UNKNOWN_CHAIN_CONFIG = {
   name: "Unknown",
@@ -50,6 +53,7 @@ const getTransport = (endpoint: string): WebSocketTransport | HttpTransport =>
 // the viem package to support new chains if they don't work as expected with the unknown
 // chain.
 const getChainById = (chainId: number): Chain =>
+  // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
   Object.values(chains).find((chain) => chain.id === chainId) ||
   defineChain({ id: chainId, ...UNKNOWN_CHAIN_CONFIG });
 

+ 18 - 9
apps/price_pusher/src/fuel/command.ts

@@ -1,14 +1,21 @@
-import { Options } from "yargs";
-import * as options from "../options";
-import { readPriceConfigFile } from "../price-config";
+/* eslint-disable @typescript-eslint/no-unsafe-call */
+/* eslint-disable @typescript-eslint/no-unsafe-argument */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import fs from "node:fs";
+
 import { HermesClient } from "@pythnetwork/hermes-client";
-import { PythPriceListener } from "../pyth-price-listener";
-import { FuelPriceListener, FuelPricePusher } from "./fuel";
-import { Controller } from "../controller";
 import { Provider, Wallet } from "fuels";
-import fs from "fs";
 import pino from "pino";
-import { filterInvalidPriceItems } from "../utils";
+import type { Options } from "yargs";
+
+import * as options from "../options.js";
+import { readPriceConfigFile } from "../price-config.js";
+import { PythPriceListener } from "../pyth-price-listener.js";
+import { FuelPriceListener, FuelPricePusher } from "./fuel.js";
+import { Controller } from "../controller.js";
+import { filterInvalidPriceItems } from "../utils.js";
+
 export default {
   command: "fuel",
   describe: "run price pusher for Fuel",
@@ -76,6 +83,8 @@ export default {
       logger.child({ module: "PythPriceListener" }),
     );
 
+    // @ts-expect-error - TODO: this dependency's typings are mismatched and there isn't a create() function on the Provider
+    // which may blow up at runtime (but this was existing behavior as of 29 Oct 2025)
     const provider = await Provider.create(endpoint);
     const privateKey = fs.readFileSync(privateKeyFile, "utf8").trim();
     const wallet = Wallet.fromPrivateKey(privateKey, provider);
@@ -104,6 +113,6 @@ export default {
       { pushingFrequency },
     );
 
-    await controller.start();
+    void controller.start();
   },
 };

+ 36 - 31
apps/price_pusher/src/fuel/fuel.ts

@@ -1,17 +1,22 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable @typescript-eslint/no-unsafe-argument */
+/* eslint-disable @typescript-eslint/no-unsafe-member-access */
+/* eslint-disable @typescript-eslint/no-unsafe-call */
+/* eslint-disable @typescript-eslint/restrict-template-expressions */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+/* eslint-disable no-console */
 import { HermesClient } from "@pythnetwork/hermes-client";
-import {
-  ChainPriceListener,
-  IPricePusher,
-  PriceInfo,
-  PriceItem,
-} from "../interface";
-import { addLeading0x, DurationInSeconds } from "../utils";
-import { Logger } from "pino";
-import { Provider, Contract, hexlify, arrayify, Wallet, BN } from "fuels";
 import {
   PYTH_CONTRACT_ABI,
   FUEL_ETH_ASSET_ID,
 } from "@pythnetwork/pyth-fuel-js";
+import { Provider, Contract, hexlify, arrayify, Wallet, BN } from "fuels";
+import type { Logger } from "pino";
+
+import type { IPricePusher, PriceInfo, PriceItem } from "../interface.js";
+import { ChainPriceListener } from "../interface.js";
+import type { DurationInSeconds } from "../utils.js";
+import { addLeading0x } from "../utils.js";
 
 // Convert TAI64 timestamp to Unix timestamp
 function tai64ToUnix(tai64: BN): number {
@@ -45,13 +50,13 @@ export class FuelPriceListener extends ChainPriceListener {
     try {
       const formattedPriceId = addLeading0x(priceId);
       const priceInfo = await this.contract.functions
-        .price_unsafe(formattedPriceId)
+        .price_unsafe?.(formattedPriceId)
         .get();
 
       console.log({
-        conf: priceInfo.value.confidence.toString(),
-        price: priceInfo.value.price.toString(),
-        publishTime: tai64ToUnix(priceInfo.value.publish_time),
+        conf: priceInfo?.value.confidence.toString(),
+        price: priceInfo?.value.price.toString(),
+        publishTime: tai64ToUnix(priceInfo?.value.publish_time),
       });
 
       this.logger.debug(
@@ -61,12 +66,15 @@ export class FuelPriceListener extends ChainPriceListener {
       );
 
       return {
-        conf: priceInfo.value.confidence.toString(),
-        price: priceInfo.value.price.toString(),
-        publishTime: tai64ToUnix(priceInfo.value.publish_time),
+        conf: priceInfo?.value.confidence.toString() ?? "",
+        price: priceInfo?.value.price.toString() ?? "",
+        publishTime: tai64ToUnix(priceInfo?.value.publish_time),
       };
-    } catch (err) {
-      this.logger.error({ err, priceId }, `Polling on-chain price failed.`);
+    } catch (error) {
+      this.logger.error(
+        { err: error, priceId },
+        `Polling on-chain price failed.`,
+      );
       return undefined;
     }
   }
@@ -88,11 +96,8 @@ export class FuelPricePusher implements IPricePusher {
     );
   }
 
-  async updatePriceFeed(
-    priceIds: string[],
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
-    pubTimesToPush: number[],
-  ): Promise<void> {
+  // eslint-disable-next-line @typescript-eslint/no-unused-vars
+  async updatePriceFeed(priceIds: string[], _: number[]): Promise<void> {
     if (priceIds.length === 0) {
       return;
     }
@@ -104,8 +109,8 @@ export class FuelPricePusher implements IPricePusher {
         ignoreInvalidPriceIds: true,
       });
       priceFeedUpdateData = response.binary.data;
-    } catch (err: any) {
-      this.logger.error(err, "getPriceFeedsUpdateData failed");
+    } catch (error: any) {
+      this.logger.error(error, "getPriceFeedsUpdateData failed");
       return;
     }
 
@@ -113,22 +118,22 @@ export class FuelPricePusher implements IPricePusher {
 
     try {
       const updateFee = await this.contract.functions
-        .update_fee(updateData)
+        .update_fee?.(updateData)
         .get();
 
       const result = await this.contract.functions
-        .update_price_feeds(updateData)
+        .update_price_feeds?.(updateData)
         .callParams({
-          forward: [updateFee.value, hexlify(FUEL_ETH_ASSET_ID)],
+          forward: [updateFee?.value, hexlify(FUEL_ETH_ASSET_ID)],
         })
         .call();
 
       this.logger.info(
-        { transactionId: result.transactionId },
+        { transactionId: result?.transactionId },
         "updatePriceFeed successful",
       );
-    } catch (err: any) {
-      this.logger.error(err, "updatePriceFeed failed");
+    } catch (error: any) {
+      this.logger.error(error, "updatePriceFeed failed");
     }
   }
 }

+ 15 - 12
apps/price_pusher/src/index.ts

@@ -1,17 +1,20 @@
-#!/usr/bin/env node
-import yargs from "yargs";
+import createCLI from "yargs";
 import { hideBin } from "yargs/helpers";
-import injective from "./injective/command";
-import evm from "./evm/command";
-import aptos from "./aptos/command";
-import sui from "./sui/command";
-import near from "./near/command";
-import solana from "./solana/command";
-import fuel from "./fuel/command";
-import ton from "./ton/command";
-import { enableMetrics, metricsPort } from "./options";
 
-yargs(hideBin(process.argv))
+import aptos from "./aptos/command.js";
+import evm from "./evm/command.js";
+import fuel from "./fuel/command.js";
+import injective from "./injective/command.js";
+import near from "./near/command.js";
+import { enableMetrics, metricsPort } from "./options.js";
+import solana from "./solana/command.js";
+import sui from "./sui/command.js";
+import ton from "./ton/command.js";
+
+const yargs = createCLI(hideBin(process.argv));
+
+// let the application run but don't await on the value here, per existing behavior
+void yargs
   .parserConfiguration({
     "parse-numbers": false,
   })

+ 17 - 12
apps/price_pusher/src/injective/command.ts

@@ -1,14 +1,19 @@
-import { HermesClient } from "@pythnetwork/hermes-client";
-import * as options from "../options";
-import { readPriceConfigFile } from "../price-config";
-import fs from "fs";
-import { InjectivePriceListener, InjectivePricePusher } from "./injective";
-import { PythPriceListener } from "../pyth-price-listener";
-import { Controller } from "../controller";
-import { Options } from "yargs";
+/* eslint-disable @typescript-eslint/no-unsafe-argument */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import fs from "node:fs";
+
 import { getNetworkInfo } from "@injectivelabs/networks";
-import pino from "pino";
-import { filterInvalidPriceItems } from "../utils";
+import { HermesClient } from "@pythnetwork/hermes-client";
+import { pino } from "pino";
+import type { Options } from "yargs";
+
+import * as options from "../options.js";
+import { readPriceConfigFile } from "../price-config.js";
+import { InjectivePriceListener, InjectivePricePusher } from "./injective.js";
+import { Controller } from "../controller.js";
+import { PythPriceListener } from "../pyth-price-listener.js";
+import { filterInvalidPriceItems } from "../utils.js";
 export default {
   command: "injective",
   describe: "run price pusher for injective",
@@ -75,7 +80,7 @@ export default {
 
     const priceConfigs = readPriceConfigFile(priceConfigFile);
     const hermesClient = new HermesClient(priceServiceEndpoint);
-    const mnemonic = fs.readFileSync(mnemonicFile, "utf-8").trim();
+    const mnemonic = fs.readFileSync(mnemonicFile, "utf8").trim();
 
     let priceItems = priceConfigs.map(({ id, alias }) => ({ id, alias }));
 
@@ -131,6 +136,6 @@ export default {
       { pushingFrequency },
     );
 
-    controller.start();
+    void controller.start();
   },
 };

+ 41 - 38
apps/price_pusher/src/injective/injective.ts

@@ -1,15 +1,11 @@
-import { HexString, HermesClient } from "@pythnetwork/hermes-client";
+/* eslint-disable @typescript-eslint/no-unsafe-call */
+/* eslint-disable @typescript-eslint/no-unsafe-member-access */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable @typescript-eslint/no-unnecessary-condition */
+/* eslint-disable @typescript-eslint/restrict-template-expressions */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+import type { Msgs, Account, TxResponse } from "@injectivelabs/sdk-ts";
 import {
-  PriceItem,
-  PriceInfo,
-  IPricePusher,
-  ChainPriceListener,
-} from "../interface";
-import { DurationInSeconds } from "../utils";
-import {
-  Msgs,
-  Account,
-  TxResponse,
   PrivateKey,
   TxGrpcApi,
   ChainGrpcAuthApi,
@@ -18,9 +14,15 @@ import {
   createTransactionFromMsg,
 } from "@injectivelabs/sdk-ts";
 import { splitArrayToChunks } from "@injectivelabs/utils";
-import { Logger } from "pino";
+import type { HexString } from "@pythnetwork/hermes-client";
+import { HermesClient } from "@pythnetwork/hermes-client";
+import type { Logger } from "pino";
+
+import type { PriceItem, PriceInfo, IPricePusher } from "../interface.js";
+import { ChainPriceListener } from "../interface.js";
+import type { DurationInSeconds } from "../utils.js";
 
-const DEFAULT_GAS_PRICE = 160000000;
+const DEFAULT_GAS_PRICE = 160_000_000;
 const DEFAULT_GAS_MULTIPLIER = 1.05;
 const DEFAULT_PRICE_IDS_PROCESS_CHUNK_SIZE = -1;
 const INJECTIVE_TESTNET_CHAIN_ID = "injective-888";
@@ -71,8 +73,8 @@ export class InjectivePriceListener extends ChainPriceListener {
 
       const json = Buffer.from(data).toString();
       priceQueryResponse = JSON.parse(json);
-    } catch (err) {
-      this.logger.error(err, `Polling on-chain price for ${priceId} failed.`);
+    } catch (error) {
+      this.logger.error(error, `Polling on-chain price for ${priceId} failed.`);
       return undefined;
     }
 
@@ -93,8 +95,7 @@ export class InjectivePriceListener extends ChainPriceListener {
 export class InjectivePricePusher implements IPricePusher {
   private mnemonic: string;
   private chainConfig: InjectiveConfig;
-  private accounts: Record<string, Account | undefined> =
-    {}; /** { address: Account } */
+  private accounts: Record<string, Account | undefined> = {};
 
   constructor(
     private hermesClient: HermesClient,
@@ -150,6 +151,7 @@ export class InjectivePricePusher implements IPricePusher {
         pubKey: wallet.toPublicKey().toBase64(),
       });
 
+      // eslint-disable-next-line @typescript-eslint/await-thenable
       const sig = await wallet.sign(Buffer.from(signBytes));
 
       /** Append Signatures */
@@ -163,13 +165,13 @@ export class InjectivePricePusher implements IPricePusher {
       account.baseAccount.sequence++;
 
       return txResponse;
-    } catch (e: any) {
+    } catch (error: any) {
       // The sequence number was invalid and hence we will have to fetch it again
-      if (JSON.stringify(e).match(/account sequence mismatch/) !== null) {
+      if (/account sequence mismatch/.exec(JSON.stringify(error)) !== null) {
         this.accounts[injectiveAddress] = undefined;
       }
 
-      throw e;
+      throw error;
     }
   }
 
@@ -224,23 +226,23 @@ export class InjectivePricePusher implements IPricePusher {
         { hash: rs.txHash },
         `Successfully broadcasted txHash for chunk ${chunkIndex}`,
       );
-    } catch (err: any) {
-      if (err.message.match(/account inj[a-zA-Z0-9]+ not found/) !== null) {
-        this.logger.error(err, `Account not found for chunk ${chunkIndex}`);
+    } catch (error: any) {
+      if (error.message.match(/account inj[a-zA-Z0-9]+ not found/) !== null) {
+        this.logger.error(error, `Account not found for chunk ${chunkIndex}`);
 
         throw new Error("Please check the mnemonic");
       }
 
       if (
-        err.message.match(/insufficient/) !== null &&
-        err.message.match(/funds/) !== null
+        error.message.match(/insufficient/) !== null &&
+        error.message.match(/funds/) !== null
       ) {
-        this.logger.error(err, `Insufficient funds for chunk ${chunkIndex}`);
+        this.logger.error(error, `Insufficient funds for chunk ${chunkIndex}`);
         throw new Error("Insufficient funds");
       }
 
       this.logger.error(
-        err,
+        error,
         `Error executing messages for chunk ${chunkIndex}`,
       );
     }
@@ -276,21 +278,21 @@ export class InjectivePricePusher implements IPricePusher {
 
       const gas = (
         result.gasInfo.gasUsed * this.chainConfig.gasMultiplier
-      ).toFixed();
+      ).toFixed(0);
       const fee = {
         amount: [
           {
             denom: "inj",
-            amount: (Number(gas) * this.chainConfig.gasPrice).toFixed(),
+            amount: (Number(gas) * this.chainConfig.gasPrice).toFixed(0),
           },
         ],
         gas,
       };
 
       return fee;
-    } catch (err) {
-      this.logger.error(err, `Error getting std fee`);
-      throw err;
+    } catch (error) {
+      this.logger.error(error, `Error getting std fee`);
+      throw error;
     }
   }
 
@@ -314,9 +316,9 @@ export class InjectivePricePusher implements IPricePusher {
           data: string[];
         };
       };
-    } catch (err) {
-      this.logger.error(err, `Error fetching the latest vaas to push`);
-      throw err;
+    } catch (error) {
+      this.logger.error(error, `Error fetching the latest vaas to push`);
+      throw error;
     }
   }
 
@@ -339,12 +341,13 @@ export class InjectivePricePusher implements IPricePusher {
 
       const json = Buffer.from(data).toString();
 
+      // eslint-disable-next-line @typescript-eslint/no-unsafe-return
       return JSON.parse(json);
-    } catch (err) {
-      this.logger.error(err, `Error fetching update fee.`);
+    } catch (error) {
+      this.logger.error(error, `Error fetching update fee.`);
 
       // Throwing an error because it is likely an RPC issue
-      throw err;
+      throw error;
     }
   }
 }

+ 17 - 16
apps/price_pusher/src/interface.ts

@@ -1,7 +1,8 @@
-import { HexString, UnixTimestamp } from "@pythnetwork/hermes-client";
-import { DurationInSeconds } from "./utils";
-import { Logger } from "pino";
-import { PricePusherMetrics } from "./metrics";
+import type { HexString, UnixTimestamp } from "@pythnetwork/hermes-client";
+import type { Logger } from "pino";
+
+import { PricePusherMetrics } from "./metrics.js";
+import type { DurationInSeconds } from "./utils.js";
 
 export type PriceItem = {
   id: HexString;
@@ -14,11 +15,11 @@ export type PriceInfo = {
   publishTime: UnixTimestamp;
 };
 
-export interface IPriceListener {
+export type IPriceListener = {
   // start fetches the latest price initially and then keep updating it
   start(): Promise<void>;
   getLatestPriceInfo(priceId: string): PriceInfo | undefined;
-}
+};
 
 export abstract class ChainPriceListener implements IPriceListener {
   private latestPriceInfo: Map<HexString, PriceInfo>;
@@ -35,7 +36,7 @@ export abstract class ChainPriceListener implements IPriceListener {
   }
 
   async start() {
-    setInterval(this.pollPrices.bind(this), this.pollingFrequency * 1000);
+    setInterval(() => void this.pollPrices(), this.pollingFrequency * 1000);
 
     await this.pollPrices();
   }
@@ -78,17 +79,17 @@ export abstract class ChainPriceListener implements IPriceListener {
   ): Promise<PriceInfo | undefined>;
 }
 
-export interface IPricePusher {
+export type IPricePusher = {
   updatePriceFeed(
     priceIds: string[],
     pubTimesToPush: UnixTimestamp[],
   ): Promise<void>;
-}
+};
 
 /**
  * Common configuration properties for all balance trackers
  */
-export interface BaseBalanceTrackerConfig {
+export type BaseBalanceTrackerConfig = {
   /** Address of the wallet to track */
   address: string;
   /** Name/ID of the network/chain */
@@ -99,13 +100,13 @@ export interface BaseBalanceTrackerConfig {
   metrics: PricePusherMetrics;
   /** Logger instance */
   logger: Logger;
-}
+};
 
 /**
  * Interface for all balance trackers to implement
  * Each chain will have its own implementation of this interface
  */
-export interface IBalanceTracker {
+export type IBalanceTracker = {
   /**
    * Start tracking the wallet balance
    */
@@ -115,7 +116,7 @@ export interface IBalanceTracker {
    * Stop tracking the wallet balance
    */
   stop(): void;
-}
+};
 
 /**
  * Abstract base class that implements common functionality for all balance trackers
@@ -126,7 +127,7 @@ export abstract class BaseBalanceTracker implements IBalanceTracker {
   protected updateInterval: DurationInSeconds;
   protected metrics: PricePusherMetrics;
   protected logger: Logger;
-  protected isRunning: boolean = false;
+  protected isRunning = false;
 
   constructor(config: BaseBalanceTrackerConfig) {
     this.address = config.address;
@@ -147,12 +148,12 @@ export abstract class BaseBalanceTracker implements IBalanceTracker {
     await this.updateBalance();
 
     // Start the update loop
-    this.startUpdateLoop();
+    void this.startUpdateLoop();
   }
 
   private async startUpdateLoop(): Promise<void> {
     // We're using dynamic import to avoid circular dependencies
-    const { sleep } = await import("./utils");
+    const { sleep } = await import("./utils.js");
 
     // Run in a loop to regularly update the balance
     for (;;) {

+ 17 - 14
apps/price_pusher/src/metrics.ts

@@ -1,7 +1,10 @@
-import { Registry, Counter, Gauge } from "prom-client";
+/* eslint-disable @typescript-eslint/restrict-template-expressions */
+/* eslint-disable @typescript-eslint/no-misused-promises */
 import express from "express";
-import { Logger } from "pino";
-import { UpdateCondition } from "./price-config";
+import type { Logger } from "pino";
+import { Registry, Counter, Gauge } from "prom-client";
+
+import { UpdateCondition } from "./price-config.js";
 
 // Define the metrics we want to track
 export class PricePusherMetrics {
@@ -10,15 +13,15 @@ export class PricePusherMetrics {
   private logger: Logger;
 
   // Metrics for price feed updates
-  public lastPublishedTime: Gauge<string>;
-  public priceUpdateAttempts: Counter<string>;
-  public priceFeedsTotal: Gauge<string>;
-  public sourceTimestamp: Gauge<string>;
-  public configuredTimeDifference: Gauge<string>;
-  public sourcePriceValue: Gauge<string>;
-  public targetPriceValue: Gauge<string>;
+  public lastPublishedTime: Gauge;
+  public priceUpdateAttempts: Counter;
+  public priceFeedsTotal: Gauge;
+  public sourceTimestamp: Gauge;
+  public configuredTimeDifference: Gauge;
+  public sourcePriceValue: Gauge;
+  public targetPriceValue: Gauge;
   // Wallet metrics
-  public walletBalance: Gauge<string>;
+  public walletBalance: Gauge;
 
   constructor(logger: Logger) {
     this.logger = logger;
@@ -86,7 +89,7 @@ export class PricePusherMetrics {
     });
 
     // Setup the metrics endpoint
-    this.server.get("/metrics", async (req, res) => {
+    this.server.get("/metrics", async (_, res) => {
       res.set("Content-Type", this.registry.contentType);
       res.end(await this.registry.metrics());
     });
@@ -103,7 +106,7 @@ export class PricePusherMetrics {
   public recordPriceUpdate(
     priceId: string,
     alias: string,
-    trigger: string = "yes",
+    trigger = "yes",
   ): void {
     this.priceUpdateAttempts.inc({
       price_id: priceId,
@@ -137,7 +140,7 @@ export class PricePusherMetrics {
   public recordPriceUpdateError(
     priceId: string,
     alias: string,
-    trigger: string = "yes",
+    trigger = "yes",
   ): void {
     this.priceUpdateAttempts.inc({
       price_id: priceId,

+ 8 - 4
apps/price_pusher/src/near/command.ts

@@ -1,11 +1,15 @@
+/* eslint-disable @typescript-eslint/no-unsafe-argument */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+/* eslint-disable @typescript-eslint/no-explicit-any */
 import { HermesClient } from "@pythnetwork/hermes-client";
+import { pino } from "pino";
+import type { Options } from "yargs";
+
+import { Controller } from "../controller";
 import * as options from "../options";
 import { readPriceConfigFile } from "../price-config";
 import { PythPriceListener } from "../pyth-price-listener";
-import { Controller } from "../controller";
-import { Options } from "yargs";
 import { NearAccount, NearPriceListener, NearPricePusher } from "./near";
-import pino from "pino";
 import { filterInvalidPriceItems } from "../utils";
 
 export default {
@@ -116,6 +120,6 @@ export default {
       { pushingFrequency },
     );
 
-    controller.start();
+    void controller.start();
   },
 };

+ 44 - 39
apps/price_pusher/src/near/near.ts

@@ -1,24 +1,27 @@
-import os from "os";
-import path from "path";
-import fs from "fs";
-
-import {
-  IPricePusher,
-  PriceInfo,
-  ChainPriceListener,
-  PriceItem,
-} from "../interface";
-import { HermesClient, HexString } from "@pythnetwork/hermes-client";
-import { DurationInSeconds } from "../utils";
-
+/* eslint-disable @typescript-eslint/no-unsafe-argument */
+/* eslint-disable unicorn/no-array-reduce */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable @typescript-eslint/no-unsafe-member-access */
+/* eslint-disable @typescript-eslint/restrict-template-expressions */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+import fs from "node:fs";
+import os from "node:os";
+import path from "node:path";
+
+import type { HexString } from "@pythnetwork/hermes-client";
+import { HermesClient } from "@pythnetwork/hermes-client";
 import { Account, Connection, KeyPair } from "near-api-js";
-import {
+import { InMemoryKeyStore } from "near-api-js/lib/key_stores";
+import type {
   ExecutionStatus,
-  ExecutionStatusBasic,
   FinalExecutionOutcome,
 } from "near-api-js/lib/providers/provider";
-import { InMemoryKeyStore } from "near-api-js/lib/key_stores";
-import { Logger } from "pino";
+import { ExecutionStatusBasic } from "near-api-js/lib/providers/provider";
+import type { Logger } from "pino";
+
+import type { IPricePusher, PriceInfo, PriceItem } from "../interface.js";
+import { ChainPriceListener } from "../interface.js";
+import type { DurationInSeconds } from "../utils.js";
 
 export class NearPriceListener extends ChainPriceListener {
   constructor(
@@ -42,17 +45,18 @@ export class NearPriceListener extends ChainPriceListener {
         )} (${priceId}) ${JSON.stringify(priceRaw)}.`,
       );
 
-      if (priceRaw) {
-        return {
-          conf: priceRaw.conf,
-          price: priceRaw.price,
-          publishTime: priceRaw.publish_time,
-        };
-      } else {
-        return undefined;
-      }
-    } catch (err) {
-      this.logger.error(err, `Polling on-chain price for ${priceId} failed.:`);
+      return priceRaw
+        ? {
+            conf: priceRaw.conf,
+            price: priceRaw.price,
+            publishTime: priceRaw.publish_time,
+          }
+        : undefined;
+    } catch (error) {
+      this.logger.error(
+        error,
+        `Polling on-chain price for ${priceId} failed.:`,
+      );
       return undefined;
     }
   }
@@ -79,8 +83,8 @@ export class NearPricePusher implements IPricePusher {
     let priceFeedUpdateData;
     try {
       priceFeedUpdateData = await this.getPriceFeedsUpdateData(priceIds);
-    } catch (err: any) {
-      this.logger.error(err, "getPriceFeedsUpdateData failed");
+    } catch (error: any) {
+      this.logger.error(error, "getPriceFeedsUpdateData failed");
       return;
     }
 
@@ -89,23 +93,23 @@ export class NearPricePusher implements IPricePusher {
       try {
         updateFee = await this.account.getUpdateFeeEstimate(data);
         this.logger.debug(`Update fee: ${updateFee}`);
-      } catch (err: any) {
-        this.logger.error(err, "getUpdateFeeEstimate failed");
+      } catch (error: any) {
+        this.logger.error(error, "getUpdateFeeEstimate failed");
         continue;
       }
 
       try {
         const outcome = await this.account.updatePriceFeeds(data, updateFee);
         const failureMessages: (ExecutionStatus | ExecutionStatusBasic)[] = [];
-        const is_success = Object.values(outcome["receipts_outcome"]).reduce(
+        const is_success = Object.values(outcome.receipts_outcome).reduce(
           (is_success, receipt) => {
             if (
               Object.prototype.hasOwnProperty.call(
-                receipt["outcome"]["status"],
+                receipt.outcome.status,
                 "Failure",
               )
             ) {
-              failureMessages.push(receipt["outcome"]["status"]);
+              failureMessages.push(receipt.outcome.status);
               return false;
             }
             return is_success;
@@ -114,14 +118,14 @@ export class NearPricePusher implements IPricePusher {
         );
         if (is_success) {
           this.logger.info(
-            { hash: outcome["transaction"]["hash"] },
+            { hash: outcome.transaction.hash },
             "updatePriceFeeds successful.",
           );
         } else {
           this.logger.error({ failureMessages }, "updatePriceFeeds failed");
         }
-      } catch (err: any) {
-        this.logger.error(err, "updatePriceFeeds failed");
+      } catch (error: any) {
+        this.logger.error(error, "updatePriceFeeds failed");
       }
     }
   }
@@ -198,6 +202,7 @@ export class NearAccount {
     privateKeyPath: string | undefined,
   ): Connection {
     const content = fs.readFileSync(
+      // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
       privateKeyPath ||
         path.join(
           os.homedir(),
@@ -214,7 +219,7 @@ export class NearAccount {
     if (accountInfo.account_id && privateKey) {
       const keyPair = KeyPair.fromString(privateKey);
       const keyStore = new InMemoryKeyStore();
-      keyStore.setKey(network, accountInfo.account_id, keyPair);
+      void keyStore.setKey(network, accountInfo.account_id, keyPair);
       return Connection.fromConfig({
         networkId: network,
         provider: { type: "JsonRpcProvider", args: { url: nodeUrl } },

+ 1 - 1
apps/price_pusher/src/options.ts

@@ -1,4 +1,4 @@
-import { Options } from "yargs";
+import type { Options } from "yargs";
 
 export const priceServiceEndpoint = {
   "price-service-endpoint": {

+ 15 - 7
apps/price_pusher/src/price-config.ts

@@ -1,10 +1,18 @@
-import { HexString } from "@pythnetwork/hermes-client";
+/* eslint-disable @typescript-eslint/restrict-template-expressions */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable @typescript-eslint/no-unsafe-argument */
+/* eslint-disable @typescript-eslint/no-unsafe-member-access */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+import fs from "node:fs";
+
+import type { HexString } from "@pythnetwork/hermes-client";
 import Joi from "joi";
+import type { Logger } from "pino";
 import YAML from "yaml";
-import fs from "fs";
-import { Logger } from "pino";
-import { DurationInSeconds, PctNumber, removeLeading0x } from "./utils";
-import { PriceInfo } from "./interface";
+
+import type { PriceInfo } from "./interface.js";
+import type { DurationInSeconds, PctNumber } from "./utils.js";
+import { removeLeading0x } from "./utils.js";
 
 const PriceConfigFileSchema: Joi.Schema = Joi.array()
   .items(
@@ -49,7 +57,7 @@ export type PriceConfig = {
 };
 
 export function readPriceConfigFile(path: string): PriceConfig[] {
-  const priceConfigs = YAML.parse(fs.readFileSync(path, "utf-8"));
+  const priceConfigs = YAML.parse(fs.readFileSync(path, "utf8"));
   const validationResult = PriceConfigFileSchema.validate(priceConfigs);
 
   if (validationResult.error !== undefined) {
@@ -85,7 +93,7 @@ export enum UpdateCondition {
 /**
  * Checks whether on-chain price needs to be updated with the latest pyth price information.
  *
- * @param priceConfig Config of the price feed to check
+ * @param priceConfig - Config of the price feed to check
  * @returns True if the on-chain price needs to be updated.
  */
 export function shouldUpdate(

+ 37 - 34
apps/price_pusher/src/pyth-price-listener.ts

@@ -1,11 +1,13 @@
-import {
-  HexString,
-  HermesClient,
-  PriceUpdate,
-} from "@pythnetwork/hermes-client";
-import { PriceInfo, IPriceListener, PriceItem } from "./interface";
-import { Logger } from "pino";
-import { sleep } from "./utils";
+/* eslint-disable no-console */
+/* eslint-disable @typescript-eslint/no-unsafe-argument */
+/* eslint-disable unicorn/prefer-add-event-listener */
+/* eslint-disable @typescript-eslint/restrict-template-expressions */
+import type { HexString, PriceUpdate } from "@pythnetwork/hermes-client";
+import { HermesClient } from "@pythnetwork/hermes-client";
+import type { Logger } from "pino";
+
+import type { PriceInfo, IPriceListener, PriceItem } from "./interface.js";
+import { sleep } from "./utils.js";
 
 type TimestampInMs = number & { readonly _: unique symbol };
 
@@ -35,7 +37,7 @@ export class PythPriceListener implements IPriceListener {
   // This method should be awaited on and once it finishes it has the latest value
   // for the given price feeds (if they exist).
   async start() {
-    this.startListening();
+    await this.startListening();
 
     // Store health check interval reference
     this.healthCheckInterval = setInterval(() => {
@@ -60,41 +62,42 @@ export class PythPriceListener implements IPriceListener {
         ignoreInvalidPriceIds: true,
       },
     );
-    eventSource.onmessage = (event: MessageEvent<string>) => {
+    eventSource.onmessage = (event: MessageEvent) => {
       const priceUpdates = JSON.parse(event.data) as PriceUpdate;
-      priceUpdates.parsed?.forEach((priceUpdate) => {
-        this.logger.debug(
-          `Received new price feed update from Pyth price service: ${this.priceIdToAlias.get(
-            priceUpdate.id,
-          )} ${priceUpdate.id}`,
-        );
+      if (priceUpdates.parsed)
+        for (const priceUpdate of priceUpdates.parsed) {
+          this.logger.debug(
+            `Received new price feed update from Pyth price service: ${this.priceIdToAlias.get(
+              priceUpdate.id,
+            )} ${priceUpdate.id}`,
+          );
 
-        // Consider price to be currently available if it is not older than 60s
-        const currentPrice =
-          Date.now() / 1000 - priceUpdate.price.publish_time > 60
-            ? undefined
-            : priceUpdate.price;
-        if (currentPrice === undefined) {
-          this.logger.debug("Price is older than 60s, skipping");
-          return;
-        }
+          // Consider price to be currently available if it is not older than 60s
+          const currentPrice =
+            Date.now() / 1000 - priceUpdate.price.publish_time > 60
+              ? undefined
+              : priceUpdate.price;
+          if (currentPrice === undefined) {
+            this.logger.debug("Price is older than 60s, skipping");
+            continue;
+          }
 
-        const priceInfo: PriceInfo = {
-          conf: currentPrice.conf,
-          price: currentPrice.price,
-          publishTime: currentPrice.publish_time,
-        };
+          const priceInfo: PriceInfo = {
+            conf: currentPrice.conf,
+            price: currentPrice.price,
+            publishTime: currentPrice.publish_time,
+          };
 
-        this.latestPriceInfo.set(priceUpdate.id, priceInfo);
-        this.lastUpdated = Date.now() as TimestampInMs;
-      });
+          this.latestPriceInfo.set(priceUpdate.id, priceInfo);
+          this.lastUpdated = Date.now() as TimestampInMs;
+        }
     };
 
     eventSource.onerror = async (error: Event) => {
       console.error("Error receiving updates from Hermes:", error);
       eventSource.close();
       await sleep(5000); // Wait a bit before trying to reconnect
-      this.startListening(); // Attempt to restart the listener
+      void this.startListening(); // Attempt to restart the listener
     };
   }
 

+ 12 - 11
apps/price_pusher/src/solana/balance-tracker.ts

@@ -1,22 +1,23 @@
 import { Connection, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";
-import {
-  BaseBalanceTracker,
+import type { Logger } from "pino";
+
+import type {
   BaseBalanceTrackerConfig,
   IBalanceTracker,
-} from "../interface";
-import { DurationInSeconds } from "../utils";
-import { PricePusherMetrics } from "../metrics";
-import { Logger } from "pino";
+} from "../interface.js";
+import { BaseBalanceTracker } from "../interface.js";
+import { PricePusherMetrics } from "../metrics.js";
+import type { DurationInSeconds } from "../utils.js";
 
 /**
  * Solana-specific configuration for balance tracker
  */
-export interface SolanaBalanceTrackerConfig extends BaseBalanceTrackerConfig {
+export type SolanaBalanceTrackerConfig = {
   /** Solana connection instance */
   connection: Connection;
   /** Solana public key */
   publicKey: PublicKey;
-}
+} & BaseBalanceTrackerConfig;
 
 /**
  * Solana-specific implementation of the balance tracker
@@ -53,7 +54,7 @@ export class SolanaBalanceTracker extends BaseBalanceTracker {
         balanceInSol,
       );
       this.logger.debug(
-        `Updated Solana wallet balance: ${this.address} = ${balanceInSol.toString()} SOL (${balanceInLamports} lamports)`,
+        `Updated Solana wallet balance: ${this.address} = ${balanceInSol.toString()} SOL (${balanceInLamports.toString()} lamports)`,
       );
     } catch (error) {
       this.logger.error(
@@ -67,14 +68,14 @@ export class SolanaBalanceTracker extends BaseBalanceTracker {
 /**
  * Parameters for creating a Solana balance tracker
  */
-export interface CreateSolanaBalanceTrackerParams {
+export type CreateSolanaBalanceTrackerParams = {
   connection: Connection;
   publicKey: PublicKey;
   network: string;
   updateInterval: DurationInSeconds;
   metrics: PricePusherMetrics;
   logger: Logger;
-}
+};
 
 /**
  * Factory function to create a balance tracker for Solana

+ 42 - 28
apps/price_pusher/src/solana/command.ts

@@ -1,29 +1,42 @@
-import { Options } from "yargs";
-import * as options from "../options";
-import { readPriceConfigFile } from "../price-config";
-import { PythPriceListener } from "../pyth-price-listener";
-import {
-  SolanaPriceListener,
-  SolanaPricePusher,
-  SolanaPricePusherJito,
-} from "./solana";
-import { Controller } from "../controller";
+/* eslint-disable @typescript-eslint/no-non-null-assertion */
+/* eslint-disable @typescript-eslint/no-unsafe-member-access */
+/* eslint-disable @typescript-eslint/no-unsafe-call */
+/* eslint-disable @typescript-eslint/restrict-template-expressions */
+/* eslint-disable @typescript-eslint/no-unsafe-argument */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import fs from "node:fs";
+
+import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet.js";
+import { HermesClient } from "@pythnetwork/hermes-client";
 import { PythSolanaReceiver } from "@pythnetwork/pyth-solana-receiver";
-import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet";
-import { Keypair, Connection, LAMPORTS_PER_SOL } from "@solana/web3.js";
-import fs from "fs";
-import { PublicKey } from "@solana/web3.js";
 import {
-  SearcherClient,
+  Keypair,
+  Connection,
+  LAMPORTS_PER_SOL,
+  PublicKey,
+} from "@solana/web3.js";
+import {
   searcherClient,
+  SearcherClient,
 } from "jito-ts/dist/sdk/block-engine/searcher";
-import pino from "pino";
-import { Logger } from "pino";
-import { HermesClient } from "@pythnetwork/hermes-client";
-import { filterInvalidPriceItems } from "../utils";
-import { PricePusherMetrics } from "../metrics";
-import { createSolanaBalanceTracker } from "./balance-tracker";
-import { IBalanceTracker } from "../interface";
+import type { Logger } from "pino";
+import { pino } from "pino";
+import type { Options } from "yargs";
+
+import * as options from "../options.js";
+import { readPriceConfigFile } from "../price-config.js";
+import { PythPriceListener } from "../pyth-price-listener.js";
+import {
+  SolanaPriceListener,
+  SolanaPricePusher,
+  SolanaPricePusherJito,
+} from "./solana.js";
+import { Controller } from "../controller.js";
+import type { IBalanceTracker } from "../interface.js";
+import { PricePusherMetrics } from "../metrics.js";
+import { filterInvalidPriceItems } from "../utils.js";
+import { createSolanaBalanceTracker } from "./balance-tracker.js";
 
 export default {
   command: "solana",
@@ -47,7 +60,7 @@ export default {
     "compute-unit-price-micro-lamports": {
       description: "Priority fee per compute unit",
       type: "number",
-      default: 50000,
+      default: 50_000,
     } as Options,
     "jito-endpoints": {
       description: "Jito endpoint(s) - comma-separated list of endpoints",
@@ -235,9 +248,9 @@ export default {
         lookupTableAccount,
       );
 
-      jitoClients.forEach((client, index) => {
+      for (const [index, client] of jitoClients.entries()) {
         onBundleResult(client, logger.child({ module: `JitoClient-${index}` }));
-      });
+      }
     } else {
       solanaPricePusher = new SolanaPricePusher(
         pythSolanaReceiver,
@@ -265,18 +278,19 @@ export default {
       logger.child({ module: "Controller" }, { level: controllerLogLevel }),
       {
         pushingFrequency,
-        metrics,
+        metrics: metrics!,
       },
     );
 
-    controller.start();
+    void controller.start();
   },
 };
 
 export const onBundleResult = (c: SearcherClient, logger: Logger) => {
   try {
     c.onBundleResult(
-      () => undefined,
+      // eslint-disable-next-line @typescript-eslint/no-empty-function
+      () => {},
       (err) => {
         logger.error(err, "Error in bundle result");
       },

+ 49 - 47
apps/price_pusher/src/solana/solana.ts

@@ -1,20 +1,19 @@
-import { PythSolanaReceiver } from "@pythnetwork/pyth-solana-receiver";
-import {
-  ChainPriceListener,
-  IPricePusher,
-  PriceInfo,
-  PriceItem,
-} from "../interface";
-import { DurationInSeconds } from "../utils";
+/* eslint-disable @typescript-eslint/no-unsafe-member-access */
+/* eslint-disable @typescript-eslint/no-explicit-any */
 import { HermesClient } from "@pythnetwork/hermes-client";
+import { sliceAccumulatorUpdateData } from "@pythnetwork/price-service-sdk";
+import { PythSolanaReceiver } from "@pythnetwork/pyth-solana-receiver";
 import {
   sendTransactions,
   sendTransactionsJito,
 } from "@pythnetwork/solana-utils";
-import { SearcherClient } from "jito-ts/dist/sdk/block-engine/searcher";
-import { sliceAccumulatorUpdateData } from "@pythnetwork/price-service-sdk";
-import { Logger } from "pino";
 import { AddressLookupTableAccount, LAMPORTS_PER_SOL } from "@solana/web3.js";
+import { SearcherClient } from "jito-ts/dist/sdk/block-engine/searcher";
+import type { Logger } from "pino";
+
+import type { IPricePusher, PriceInfo, PriceItem } from "../interface.js";
+import { ChainPriceListener } from "../interface.js";
+import type { DurationInSeconds } from "../utils.js";
 
 const HEALTH_CHECK_TIMEOUT_SECONDS = 60;
 
@@ -39,25 +38,25 @@ export class SolanaPriceListener extends ChainPriceListener {
       const blockTime =
         await this.pythSolanaReceiver.connection.getBlockTime(slot);
       if (
-        blockTime === null ||
-        blockTime < Date.now() / 1000 - HEALTH_CHECK_TIMEOUT_SECONDS
+        (blockTime === null ||
+          blockTime < Date.now() / 1000 - HEALTH_CHECK_TIMEOUT_SECONDS) &&
+        blockTime !== null
       ) {
-        if (blockTime !== null) {
-          this.logger.info(
-            `Solana connection is behind by ${
-              Date.now() / 1000 - blockTime
-            } seconds`,
-          );
-        }
+        this.logger.info(
+          `Solana connection is behind by ${(
+            Date.now() / 1000 -
+            blockTime
+          ).toString()} seconds`,
+        );
       }
-    } catch (err) {
-      this.logger.error({ err }, "checkHealth failed");
+    } catch (error) {
+      this.logger.error({ err: error }, "checkHealth failed");
     }
   }
 
-  async start() {
+  override async start() {
     // Frequently check the RPC connection to ensure it is healthy
-    setInterval(this.checkHealth.bind(this), 5000);
+    setInterval(() => void this.checkHealth(), 5000);
 
     await super.start();
   }
@@ -70,21 +69,22 @@ export class SolanaPriceListener extends ChainPriceListener {
           Buffer.from(priceId, "hex"),
         );
       this.logger.debug(
-        `Polled a Solana on chain price for feed ${this.priceIdToAlias.get(
-          priceId,
-        )} (${priceId}).`,
+        `Polled a Solana on chain price for feed ${
+          this.priceIdToAlias.get(priceId)?.toString() ?? ""
+        } (${priceId}).`,
+      );
+      return priceFeedAccount
+        ? {
+            conf: priceFeedAccount.priceMessage.conf.toString(),
+            price: priceFeedAccount.priceMessage.price.toString(),
+            publishTime: priceFeedAccount.priceMessage.publishTime.toNumber(),
+          }
+        : undefined;
+    } catch (error) {
+      this.logger.error(
+        { err: error, priceId },
+        `Polling on-chain price failed.`,
       );
-      if (priceFeedAccount) {
-        return {
-          conf: priceFeedAccount.priceMessage.conf.toString(),
-          price: priceFeedAccount.priceMessage.price.toString(),
-          publishTime: priceFeedAccount.priceMessage.publishTime.toNumber(),
-        };
-      } else {
-        return undefined;
-      }
-    } catch (err) {
-      this.logger.error({ err, priceId }, `Polling on-chain price failed.`);
       return undefined;
     }
   }
@@ -122,8 +122,8 @@ export class SolanaPricePusher implements IPricePusher {
         },
       );
       priceFeedUpdateData = response.binary.data;
-    } catch (err: any) {
-      this.logger.error(err, "getPriceFeedsUpdateData failed:");
+    } catch (error: any) {
+      this.logger.error(error, "getPriceFeedsUpdateData failed:");
       return;
     }
 
@@ -150,8 +150,8 @@ export class SolanaPricePusher implements IPricePusher {
         this.pythSolanaReceiver.wallet,
       );
       this.logger.info({ signatures }, "updatePriceFeed successful");
-    } catch (err: any) {
-      this.logger.error(err, "updatePriceFeed failed");
+    } catch (error: any) {
+      this.logger.error(error, "updatePriceFeed failed");
       return;
     }
   }
@@ -184,12 +184,14 @@ export class SolanaPricePusherJito implements IPricePusher {
         );
         return undefined;
       }
-      const data = await response.json();
+      // TODO: fix this type here to be more specific
+      const data = (await response.json()) as any[];
       return Math.floor(
         Number(data[0].landed_tips_50th_percentile) * LAMPORTS_PER_SOL,
       );
-    } catch (err: any) {
-      this.logger.error({ err }, "getRecentJitoTips failed");
+    } catch (error: any) {
+      // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+      this.logger.error({ err: error }, "getRecentJitoTips failed");
       return undefined;
     }
   }
@@ -210,8 +212,8 @@ export class SolanaPricePusherJito implements IPricePusher {
         encoding: "base64",
       });
       priceFeedUpdateData = response.binary.data;
-    } catch (err: any) {
-      this.logger.error(err, "getPriceFeedsUpdateData failed");
+    } catch (error: any) {
+      this.logger.error(error, "getPriceFeedsUpdateData failed");
       return;
     }
 

+ 12 - 11
apps/price_pusher/src/sui/balance-tracker.ts

@@ -1,20 +1,21 @@
 import { SuiClient } from "@mysten/sui/client";
-import {
-  BaseBalanceTracker,
+import type { Logger } from "pino";
+
+import type {
   BaseBalanceTrackerConfig,
   IBalanceTracker,
-} from "../interface";
-import { DurationInSeconds } from "../utils";
-import { PricePusherMetrics } from "../metrics";
-import { Logger } from "pino";
+} from "../interface.js";
+import { BaseBalanceTracker } from "../interface.js";
+import { PricePusherMetrics } from "../metrics.js";
+import type { DurationInSeconds } from "../utils.js";
 
 /**
  * Sui-specific configuration for balance tracker
  */
-export interface SuiBalanceTrackerConfig extends BaseBalanceTrackerConfig {
+export type SuiBalanceTrackerConfig = {
   /** Sui client instance */
   client: SuiClient;
-}
+} & BaseBalanceTrackerConfig;
 
 /**
  * Sui-specific implementation of the balance tracker
@@ -50,7 +51,7 @@ export class SuiBalanceTracker extends BaseBalanceTracker {
       );
 
       this.logger.debug(
-        `Updated Sui wallet balance: ${this.address} = ${normalizedBalance} SUI`,
+        `Updated Sui wallet balance: ${this.address} = ${normalizedBalance.toString()} SUI`,
       );
     } catch (error) {
       this.logger.error(
@@ -64,14 +65,14 @@ export class SuiBalanceTracker extends BaseBalanceTracker {
 /**
  * Parameters for creating a Sui balance tracker
  */
-export interface CreateSuiBalanceTrackerParams {
+export type CreateSuiBalanceTrackerParams = {
   client: SuiClient;
   address: string;
   network: string;
   updateInterval: DurationInSeconds;
   metrics: PricePusherMetrics;
   logger: Logger;
-}
+};
 
 /**
  * Factory function to create a balance tracker for Sui chain

Some files were not shown because too many files changed in this diff