Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions apps/entropy-debug/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$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": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
5 changes: 5 additions & 0 deletions apps/entropy-debug/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
51 changes: 51 additions & 0 deletions apps/entropy-debug/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"name": "entropy-debug-app",
"version": "0.1.0",
"description": "Debugging tool for entropy",
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/pyth-network/pyth-crosschain",
"directory": "apps/entropy-debug"
},
"publishConfig": {
"access": "public"
},
"scripts": {
"dev": "next dev --port 3005",
"build": "next build",
"start": "next start --port 3005",
"lint": "next lint"
},
"license": "Apache-2.0",
"dependencies": {
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"ethers": "^6.13.4",
"highlight.js": "^11.10.0",
"lucide-react": "^0.465.0",
"next": "catalog:",
"react": "catalog:",
"react-dom": "catalog:",
"tailwind-merge": "^2.5.5",
"tailwindcss-animate": "^1.0.7",
"viem": "^2.21.53"
},
"devDependencies": {
"@cprussin/eslint-config": "catalog:",
"@cprussin/jest-config": "catalog:",
"@cprussin/prettier-config": "catalog:",
"@cprussin/tsconfig": "catalog:",
"@types/node": "^20",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"eslint": "^8",
"eslint-config-next": "15.0.3",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
}
8 changes: 8 additions & 0 deletions apps/entropy-debug/postcss.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
};

export default config;
Binary file added apps/entropy-debug/src/app/favicon.ico
Binary file not shown.
Binary file added apps/entropy-debug/src/app/fonts/GeistMonoVF.woff
Binary file not shown.
Binary file added apps/entropy-debug/src/app/fonts/GeistVF.woff
Binary file not shown.
72 changes: 72 additions & 0 deletions apps/entropy-debug/src/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
@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;
}
}
35 changes: 35 additions & 0 deletions apps/entropy-debug/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
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>
);
}
197 changes: 197 additions & 0 deletions apps/entropy-debug/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
"use client";
import * as React from "react";
import { Switch } from "../components/ui/switch";
import { Input } from "../components/ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "../components/ui/select";
import { useState, useMemo, useCallback, useEffect, useRef } from "react";
import { EntropyDeployments } from "../store/EntropyDeployments";
import { isValidTxHash } from "../lib/utils";
import { requestCallback } from "../lib/revelation";
import hljs from "highlight.js/lib/core";
import bash from "highlight.js/lib/languages/bash";
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 }),
Error: (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.Error>;

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 | null>(null);
const [selectedChain, setSelectedChain] = useState<string>("");

const validateTxHash = (hash: string) => {
if (!isValidTxHash(hash) && hash !== "") {
setError(
new InvalidTxHashError(
"Transaction hash must be 64 hexadecimal characters"
)
);
} else {
setError(null);
}
setTxHash(hash);
};

const availableChains = useMemo(() => {
return Object.entries(EntropyDeployments)
.filter(
([, deployment]) =>
deployment.network === (isMainnet ? "mainnet" : "testnet")
)
.map(([key]) => key);
}, [isMainnet]);

const oncClickFetchInfo = useCallback(() => {
setState(TxState.Loading());
requestCallback(txHash, selectedChain)
.then((data) => {
setState(TxState.Success(data));
})
.catch((error) => {
setState(TxState.Error(error));
});
}, [txHash, selectedChain]);

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)}
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>
);
}
};

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={setIsMainnet}
/>
<label htmlFor="network-mode">Mainnet</label>
</div>
<div className="mt-4">
<Select onValueChange={setSelectedChain} value={selectedChain}>
<SelectTrigger>
<SelectValue placeholder="Select Chain" />
</SelectTrigger>
<SelectContent>
{availableChains.map((chain) => (
<SelectItem key={chain} value={chain}>
{chain.charAt(0).toUpperCase() +
chain.slice(1).replace(/-/g, " ")}
</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 Request 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 text-white p-2 rounded"
onClick={oncClickFetchInfo}
>
Fetch Info
</button>
</div>
<Info state={state} />
</div>
);
}
Loading
Loading