| 
 | 1 | +"use client"  | 
 | 2 | + | 
 | 3 | +import * as React from "react"  | 
 | 4 | +import { Switch } from "../components/ui/switch"  | 
 | 5 | +import { Input } from "../components/ui/input"  | 
 | 6 | +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../components/ui/select"  | 
 | 7 | +import { useState, useMemo, useCallback, useEffect, useRef } from "react"  | 
 | 8 | +import { EntropyDeployments } from "../store/EntropyDeployments"  | 
 | 9 | +import { isValidTxHash } from "../lib/utils"  | 
 | 10 | +import { requestCallback } from "../lib/revelation"  | 
 | 11 | +import hljs from 'highlight.js/lib/core';  | 
 | 12 | +import bash from 'highlight.js/lib/languages/bash';  | 
 | 13 | +import 'highlight.js/styles/github-dark.css'; // You can choose different themes  | 
 | 14 | + | 
 | 15 | +// Register the bash language  | 
 | 16 | +hljs.registerLanguage('bash', bash);  | 
 | 17 | + | 
 | 18 | +class BaseError extends Error {  | 
 | 19 | +  constructor(message: string) {  | 
 | 20 | +    super(message)  | 
 | 21 | +    this.name = "BaseError"  | 
 | 22 | +  }  | 
 | 23 | +}  | 
 | 24 | + | 
 | 25 | +class InvalidTxHashError extends BaseError {  | 
 | 26 | +  constructor(message: string) {  | 
 | 27 | +    super(message)  | 
 | 28 | +    this.name = "InvalidTxHashError"  | 
 | 29 | +  }  | 
 | 30 | +}  | 
 | 31 | + | 
 | 32 | + | 
 | 33 | +enum TxStateType {  | 
 | 34 | +  NotLoaded,  | 
 | 35 | +  Loading,  | 
 | 36 | +  Success,  | 
 | 37 | +  Error  | 
 | 38 | +}  | 
 | 39 | + | 
 | 40 | +const TxState = {  | 
 | 41 | +  NotLoaded: () => ({status: TxStateType.NotLoaded as const}),  | 
 | 42 | +  Loading: () => ({status: TxStateType.Loading as const}),  | 
 | 43 | +  Success: (data: string) => ({status: TxStateType.Success as const, data}),  | 
 | 44 | +  Error: (error: unknown) => ({status: TxStateType.Error as const, error}),  | 
 | 45 | +}  | 
 | 46 | + | 
 | 47 | +type TxStateContext = ReturnType<typeof TxState.NotLoaded> | ReturnType<typeof TxState.Loading> | ReturnType<typeof TxState.Success> | ReturnType<typeof TxState.Error>  | 
 | 48 | + | 
 | 49 | +export default function PythEntropyDebugApp() {  | 
 | 50 | +  const [state, setState] = useState<TxStateContext>(TxState.NotLoaded());  | 
 | 51 | +  const [isMainnet, setIsMainnet] = useState<boolean>(false);  | 
 | 52 | +  const [txHash, setTxHash] = useState<string>("");  | 
 | 53 | +  const [error, setError] = useState<BaseError | null>(null);  | 
 | 54 | +  const [selectedChain, setSelectedChain] = useState<string>("");  | 
 | 55 | + | 
 | 56 | +  const validateTxHash = (hash: string) => {  | 
 | 57 | +    if (!isValidTxHash(hash) && hash !== "") {  | 
 | 58 | +      setError(new InvalidTxHashError("Transaction hash must be 64 hexadecimal characters"));  | 
 | 59 | +    } else {  | 
 | 60 | +      setError(null);  | 
 | 61 | +    }  | 
 | 62 | +    setTxHash(hash);  | 
 | 63 | +  };  | 
 | 64 | + | 
 | 65 | +  const availableChains = useMemo(() => {  | 
 | 66 | +    return Object.entries(EntropyDeployments)  | 
 | 67 | +      .filter(([, deployment]) => deployment.network === (isMainnet ? "mainnet" : "testnet"))  | 
 | 68 | +      .map(([key]) => key);  | 
 | 69 | +  }, [isMainnet]);  | 
 | 70 | + | 
 | 71 | +  const oncClickFetchInfo = useCallback(() => {  | 
 | 72 | +    setState(TxState.Loading());  | 
 | 73 | +    requestCallback(txHash, selectedChain)  | 
 | 74 | +      .then((data) => {  | 
 | 75 | +        setState(TxState.Success(data));  | 
 | 76 | +      })  | 
 | 77 | +      .catch((error) => {  | 
 | 78 | +        setState(TxState.Error(error));  | 
 | 79 | +      });  | 
 | 80 | +  }, [txHash, selectedChain]);  | 
 | 81 | + | 
 | 82 | +  const Info = ({state}: {state: TxStateContext}) => {  | 
 | 83 | +    const preRef = useRef<HTMLPreElement>(null);  | 
 | 84 | + | 
 | 85 | +    useEffect(() => {  | 
 | 86 | +      if (preRef.current && state.status === TxStateType.Success) {  | 
 | 87 | +        hljs.highlightElement(preRef.current);  | 
 | 88 | +      }  | 
 | 89 | +    }, [state]);  | 
 | 90 | + | 
 | 91 | +    switch (state.status) {  | 
 | 92 | +      case TxStateType.NotLoaded:  | 
 | 93 | +        return <div>Not loaded</div>  | 
 | 94 | +      case TxStateType.Loading:  | 
 | 95 | +        return <div>Loading...</div>  | 
 | 96 | +      case TxStateType.Success:  | 
 | 97 | +        return (  | 
 | 98 | +          <div className="mt-4 p-4 bg-gray-100 rounded w-full max-w-3xl">  | 
 | 99 | +            <p className="mb-2">Please run the following command in your terminal:</p>  | 
 | 100 | +            <div className="relative">  | 
 | 101 | +              <pre  | 
 | 102 | +                ref={preRef}  | 
 | 103 | +                className="bg-black text-white p-4 rounded overflow-x-auto whitespace-pre-wrap break-words"  | 
 | 104 | +              >  | 
 | 105 | +                <code className="language-bash">{state.data}</code>  | 
 | 106 | +              </pre>  | 
 | 107 | +              <button  | 
 | 108 | +                onClick={() => navigator.clipboard.writeText(state.data)}  | 
 | 109 | +                className="absolute top-2 right-2 bg-gray-700 text-white px-3 py-1 rounded hover:bg-gray-600"  | 
 | 110 | +              >  | 
 | 111 | +                Copy  | 
 | 112 | +              </button>  | 
 | 113 | +            </div>  | 
 | 114 | +          </div>  | 
 | 115 | +        )  | 
 | 116 | +      case TxStateType.Error:  | 
 | 117 | +        return (  | 
 | 118 | +          <div className="mt-4 p-4 bg-red-100 border border-red-400 rounded">  | 
 | 119 | +            <div className="text-red-600">{String(state.error)}</div>  | 
 | 120 | +          </div>  | 
 | 121 | +        )  | 
 | 122 | +    }  | 
 | 123 | +  }  | 
 | 124 | + | 
 | 125 | +  return (  | 
 | 126 | +    <div className="flex flex-col items-center justify-start h-screen">  | 
 | 127 | +      <h1 className="text-4xl font-bold mt-8">Pyth Entropy Debug App</h1>  | 
 | 128 | + | 
 | 129 | +      <div className="flex items-center space-x-2 mt-4">  | 
 | 130 | +        <label htmlFor="network-mode">Testnet</label>  | 
 | 131 | +        <Switch  | 
 | 132 | +          id="network-mode"  | 
 | 133 | +          defaultChecked={false}  | 
 | 134 | +          onCheckedChange={setIsMainnet}  | 
 | 135 | +        />  | 
 | 136 | +        <label htmlFor="network-mode">Mainnet</label>  | 
 | 137 | +      </div>  | 
 | 138 | +      <div className="mt-4">  | 
 | 139 | +          <Select onValueChange={setSelectedChain} value={selectedChain}>  | 
 | 140 | +          <SelectTrigger>  | 
 | 141 | +            <SelectValue placeholder="Select Chain" />  | 
 | 142 | +          </SelectTrigger>  | 
 | 143 | +          <SelectContent>  | 
 | 144 | +            {availableChains.map((chain) => (  | 
 | 145 | +              <SelectItem key={chain} value={chain}>  | 
 | 146 | +                {chain.charAt(0).toUpperCase() + chain.slice(1).replace(/-/g, ' ')}  | 
 | 147 | +              </SelectItem>  | 
 | 148 | +            ))}  | 
 | 149 | +          </SelectContent>  | 
 | 150 | +        </Select>  | 
 | 151 | +      </div>  | 
 | 152 | +      <div className="mt-4">  | 
 | 153 | +        <label htmlFor="tx-hash" className="mr-2">Request Transaction Hash:</label>  | 
 | 154 | +        <Input  | 
 | 155 | +          minLength={64}  | 
 | 156 | +          id="tx-hash"  | 
 | 157 | +          className={`border rounded p-2 w-full ${error ? 'border-red-500' : ''}`}  | 
 | 158 | +          placeholder="Enter Request Transaction Hash:"  | 
 | 159 | +          value={txHash}  | 
 | 160 | +          onChange={(e) => validateTxHash(e.target.value)}  | 
 | 161 | +        />  | 
 | 162 | +        {error && <p className="text-red-500 text-sm mt-1">{error.message}</p>}  | 
 | 163 | +      </div>  | 
 | 164 | +      <div className="mt-4">  | 
 | 165 | +        <button  | 
 | 166 | +          className="bg-blue-500 text-white p-2 rounded"  | 
 | 167 | +          onClick={oncClickFetchInfo}  | 
 | 168 | +        >  | 
 | 169 | +          Fetch Info  | 
 | 170 | +        </button>  | 
 | 171 | +      </div>  | 
 | 172 | +      <Info state={state} />  | 
 | 173 | +    </div>  | 
 | 174 | +  );  | 
 | 175 | +}  | 
0 commit comments