Przeglądaj źródła

chore(dev-docs) Entropy Contract Address table

Aditya Arora 2 miesięcy temu
rodzic
commit
86c3dfb750

+ 4 - 22
apps/developer-hub/content/docs/entropy/contract-addresses.mdx

@@ -1,23 +1,15 @@
 ---
 title: Contract Addresses
-description: Entropy contract addresses on different networks
+description: Pyth Entropy contract addresses on EVM networks
 ---
 
-# Entropy Contract Addresses on EVM
+import { EntropyTable } from "../../../src/components/EntropyTable";
 
 ## Mainnets
 
 The Entropy contract is deployed on the following mainnet chains:
 
-| Network   | Contract Address                             | Gas Limit | Provider                                     |
-| --------- | -------------------------------------------- | --------- | -------------------------------------------- |
-| Ethereum  | `0x98046Bd286715D3B0BC227Dd7a956b83D8978603` | 100,000   | `0x52DeaA1c84233F7bb8C8A45baeDE41091c616506` |
-| Arbitrum  | `0x23f0e8FAeE7bbb405E7A7C3d60138FCfd43d7509` | 100,000   | `0x52DeaA1c84233F7bb8C8A45baeDE41091c616506` |
-| Avalanche | `0x36825bf3Fbdf5a29E2d5148bfe7Dcf7B5639e320` | 100,000   | `0x52DeaA1c84233F7bb8C8A45baeDE41091c616506` |
-| Base      | `0x98046Bd286715D3B0BC227Dd7a956b83D8978603` | 100,000   | `0x52DeaA1c84233F7bb8C8A45baeDE41091c616506` |
-| BNB Chain | `0x98046Bd286715D3B0BC227Dd7a956b83D8978603` | 100,000   | `0x52DeaA1c84233F7bb8C8A45baeDE41091c616506` |
-| Optimism  | `0x98046Bd286715D3B0BC227Dd7a956b83D8978603` | 100,000   | `0x52DeaA1c84233F7bb8C8A45baeDE41091c616506` |
-| Polygon   | `0x98046Bd286715D3B0BC227Dd7a956b83D8978603` | 100,000   | `0x52DeaA1c84233F7bb8C8A45baeDE41091c616506` |
+<EntropyTable isMainnet={true} />
 
 **The default provider for above mainnet chains is `0x52DeaA1c84233F7bb8C8A45baeDE41091c616506`.**
 
@@ -30,18 +22,8 @@ The default provider fulfills the request by sending a transaction with a gas li
 
 The Entropy contract is deployed on the following testnet chains:
 
-| Network          | Contract Address                             | Gas Limit | Provider                                     |
-| ---------------- | -------------------------------------------- | --------- | -------------------------------------------- |
-| Ethereum Sepolia | `0x41c9e39574F40Ad34c79f1C99B66A45eFB830d4c` | 100,000   | `0x6CC14824Ea2918f5De5C2f75A9Da968ad4BD6344` |
-| Arbitrum Sepolia | `0x36825bf3Fbdf5a29E2d5148bfe7Dcf7B5639e320` | 100,000   | `0x6CC14824Ea2918f5De5C2f75A9Da968ad4BD6344` |
-| Avalanche Fuji   | `0x36825bf3Fbdf5a29E2d5148bfe7Dcf7B5639e320` | 100,000   | `0x6CC14824Ea2918f5De5C2f75A9Da968ad4BD6344` |
-| Base Sepolia     | `0x41c9e39574F40Ad34c79f1C99B66A45eFB830d4c` | 100,000   | `0x6CC14824Ea2918f5De5C2f75A9Da968ad4BD6344` |
-| BNB Testnet      | `0x41c9e39574F40Ad34c79f1C99B66A45eFB830d4c` | 100,000   | `0x6CC14824Ea2918f5De5C2f75A9Da968ad4BD6344` |
-| Optimism Sepolia | `0x41c9e39574F40Ad34c79f1C99B66A45eFB830d4c` | 100,000   | `0x6CC14824Ea2918f5De5C2f75A9Da968ad4BD6344` |
-| Polygon Amoy     | `0x41c9e39574F40Ad34c79f1C99B66A45eFB830d4c` | 100,000   | `0x6CC14824Ea2918f5De5C2f75A9Da968ad4BD6344` |
+<EntropyTable isMainnet={false} />
 
 **The default provider for above testnet chains is `0x6CC14824Ea2918f5De5C2f75A9Da968ad4BD6344`.**
 
 The default provider on testnet has reveal delays identical to the corresponding mainnet chains to ensure consistent environment.
-
-The default provider fulfills the request by sending a transaction with a gas limit as mentioned in above table. Entropy callbacks the consumer as part of this transaction.

+ 20 - 13
apps/developer-hub/content/docs/entropy/debug-callback-failures.mdx

@@ -2,43 +2,48 @@
 title: Debug Callback Failures
 description: How to identify and resolve issues with Entropy callbacks
 ---
-import { DynamicCodeBlock } from 'fumadocs-ui/components/dynamic-codeblock';
+
+import { DynamicCodeBlock } from "fumadocs-ui/components/dynamic-codeblock";
 
 This guide explains how to identify and resolve issues with the Entropy callback.
 The intended audience for this guide is developers who have made an Entropy random number request, but their application hasn't received a callback.
 
 > 🔍 **Quick Debug Tool**
-> 
+>
 > Use the [Entropy Explorer](https://entropy-debugger.pyth.network/) to quickly diagnose and resolve callback issues.
 
-
 ## Dependencies
 
 This guide uses [Foundry](https://book.getfoundry.sh/getting-started/installation) to submit transactions to the blockchain.
 Please install Foundry before continuing.
+
 ## Run the Callback
 
 Developers can run the Entropy callback themselves to see the reason for the failure.
 To run the callback, invoke the `revealWithCallback` function on the Entropy contract on your blockchain.
 The function has the following signature:
 
-<DynamicCodeBlock lang="solidity" 
-code={`function revealWithCallback(
+<DynamicCodeBlock
+  lang="solidity"
+  code={`function revealWithCallback(
     address provider,
     uint64 sequenceNumber,
     bytes32 userContribution,
     bytes32 providerContribution
 )
-`} />
+`}
+/>
 
 This call requires the chain ID, contract address, and four other arguments.
 The chain ID and contract address can be retrieved from [Contract Addresses](contract-addresses).
 Export these values as environment variables for later use:
 
-<DynamicCodeBlock lang="bash" 
-code={`export CHAIN_ID=blast
+<DynamicCodeBlock
+  lang="bash"
+  code={`export CHAIN_ID=blast
 export ENTROPY_ADDRESS=0x5744Cbf430D99456a0A8771208b674F27f8EF0Fb
-`} />
+`}
+/>
 
 Three of the other arguments can be retrieved from the request transaction's event logs.
 Look at the event logs of the request transaction in a block explorer.
@@ -46,11 +51,13 @@ You should see a `RequestedWithCallback` event emitted from the Entropy contract
 
 Copy the following values from the event into environment variables:
 
-<DynamicCodeBlock lang="bash" 
-code={`export PROVIDER=0x52DeaA1c84233F7bb8C8A45baeDE41091c616506
+<DynamicCodeBlock
+  lang="bash"
+  code={`export PROVIDER=0x52DeaA1c84233F7bb8C8A45baeDE41091c616506
 export SEQUENCE_NUMBER=12345
 export USER_RANDOM_NUMBER=0x1234...
-`} />
+`}
+/>
 
 The fourth argument (provider contribution) must be retrieved from the provider's API.
 This value becomes available after the reveal delay has passed.
@@ -78,4 +85,4 @@ Your callback function might contain logic that throws an error. Review your cal
 ### Transaction Timing
 
 Make sure you're attempting the callback after the reveal delay has passed. The reveal delay varies by network and helps prevent MEV attacks.
-Refer to the [Contract Addresses](contract-addresses) page for the reveal delay for each network.
+Refer to the [Contract Addresses](contract-addresses) page for the reveal delay for each network.

+ 48 - 43
apps/developer-hub/content/docs/entropy/generate-random-numbers-evm.mdx

@@ -4,7 +4,7 @@ description: Learn how to integrate Pyth Entropy to generate random numbers in y
 ---
 
 import { Step, Steps } from "fumadocs-ui/components/steps";
-import { DynamicCodeBlock } from 'fumadocs-ui/components/dynamic-codeblock';
+import { DynamicCodeBlock } from "fumadocs-ui/components/dynamic-codeblock";
 
 This guide explains how to integrate Pyth Entropy into EVM Contracts to generate on-chain random numbers.
 The intended audience for this guide is developers of any application that needs on-chain randomness, such as NFT mints or games.
@@ -26,9 +26,11 @@ npm install @pythnetwork/entropy-sdk-solidity
 
 Then add the following line to your `remappings.txt` file:
 
-<DynamicCodeBlock lang="text" 
-code={`@pythnetwork/entropy-sdk-solidity/=node_modules/@pythnetwork/entropy-sdk-solidity
-`} />
+<DynamicCodeBlock
+  lang="text"
+  code={`@pythnetwork/entropy-sdk-solidity/=node_modules/@pythnetwork/entropy-sdk-solidity
+`}
+/>
 
 </Tab>
 </Tabs>
@@ -49,11 +51,11 @@ import { IEntropyV2 } from "@pythnetwork/entropy-sdk-solidity/IEntropyV2.sol";
 
 // @param entropyAddress The address of the entropy contract.
 contract YourContract is IEntropyConsumer {
-  IEntropyV2 public entropy;
+IEntropyV2 public entropy;
 
-  constructor(address entropyAddress) {
-    entropy = IEntropyV2(entropyAddress);
-  }
+constructor(address entropyAddress) {
+entropy = IEntropyV2(entropyAddress);
+}
 }
 `} />
 
@@ -78,7 +80,7 @@ These methods use the default randomness provider ([see here](#randomness-provid
 code={`function requestRandomNumber() external payable {
   uint256 fee = entropy.getFeeV2();
 
-  uint64 sequenceNumber = entropy.requestV2{ value: fee }();
+uint64 sequenceNumber = entropy.requestV2{ value: fee }();
 }
 `} />
 
@@ -100,43 +102,44 @@ import { IEntropyConsumer } from "@pythnetwork/entropy-sdk-solidity/IEntropyCons
 import { IEntropyV2 } from "@pythnetwork/entropy-sdk-solidity/IEntropyV2.sol";
 
 contract YourContract is IEntropyConsumer {
-  IEntropyV2 entropy;
+IEntropyV2 entropy;
 
-  // @param entropyAddress The address of the entropy contract.
-  constructor(address entropyAddress) {
-    entropy = IEntropyV2(entropyAddress);
-  }
+// @param entropyAddress The address of the entropy contract.
+constructor(address entropyAddress) {
+entropy = IEntropyV2(entropyAddress);
+}
 
-  function requestRandomNumber() external payable {
-    // Get the fee for the request
-    uint256 fee = entropy.getFeeV2();
+function requestRandomNumber() external payable {
+// Get the fee for the request
+uint256 fee = entropy.getFeeV2();
 
     // Request the random number with the callback
     uint64 sequenceNumber = entropy.requestV2{ value: fee }();
     // Store the sequence number to identify the callback request
-  }
-
-  // @param sequenceNumber The sequence number of the request.
-  // @param provider The address of the provider that generated the random number. If your app uses multiple providers, you can use this argument to distinguish which one is calling the app back.
-  // @param randomNumber The generated random number.
-  // This method is called by the entropy contract when a random number is generated.
-  // This method **must** be implemented on the same contract that requested the random number.
-  // This method should **never** return an error -- if it returns an error, then the keeper will not be able to invoke the callback.
-  // If you are having problems receiving the callback, the most likely cause is that the callback is erroring.
-  // See the callback debugging guide here to identify the error https://docs.pyth.network/entropy/debug-callback-failures
-  function entropyCallback(
-    uint64 sequenceNumber,
-    address provider,
-    bytes32 randomNumber
-  ) internal override {
-    // Implement your callback logic here.
-  }
-
-  // This method is required by the IEntropyConsumer interface.
-  // It returns the address of the entropy contract which will call the callback.
-  function getEntropy() internal view override returns (address) {
-    return address(entropy);
-  }
+
+}
+
+// @param sequenceNumber The sequence number of the request.
+// @param provider The address of the provider that generated the random number. If your app uses multiple providers, you can use this argument to distinguish which one is calling the app back.
+// @param randomNumber The generated random number.
+// This method is called by the entropy contract when a random number is generated.
+// This method **must** be implemented on the same contract that requested the random number.
+// This method should **never** return an error -- if it returns an error, then the keeper will not be able to invoke the callback.
+// If you are having problems receiving the callback, the most likely cause is that the callback is erroring.
+// See the callback debugging guide here to identify the error https://docs.pyth.network/entropy/debug-callback-failures
+function entropyCallback(
+uint64 sequenceNumber,
+address provider,
+bytes32 randomNumber
+) internal override {
+// Implement your callback logic here.
+}
+
+// This method is required by the IEntropyConsumer interface.
+// It returns the address of the entropy contract which will call the callback.
+function getEntropy() internal view override returns (address) {
+return address(entropy);
+}
 }
 `} />
 
@@ -180,6 +183,8 @@ a keeper service for fullfilling requests.
 
 You can get the default provider's address by calling the [`getDefaultProvider`](https://github.com/pyth-network/pyth-crosschain/blob/f8ebeb6af31d98f94ce73edade6da2ebab7b2456/target_chains/ethereum/entropy_sdk/solidity/IEntropy.sol#L94) method:
 
-<DynamicCodeBlock lang="solidity" 
-code={`address provider = entropy.getDefaultProvider();
-`} />
+<DynamicCodeBlock
+  lang="solidity"
+  code={`address provider = entropy.getDefaultProvider();
+`}
+/>

+ 58 - 49
apps/developer-hub/content/docs/entropy/set-custom-gas-limits.mdx

@@ -4,7 +4,7 @@ description: How to set custom gas limits for Entropy callbacks
 ---
 
 import { Step, Steps } from "fumadocs-ui/components/steps";
-import { DynamicCodeBlock } from 'fumadocs-ui/components/dynamic-codeblock';
+import { DynamicCodeBlock } from "fumadocs-ui/components/dynamic-codeblock";
 
 Custom gas limits are useful when your callback function requires more gas than the [default provider limit]TODO(../contract-addresses), or when you want to optimize gas costs for simpler callbacks.
 
@@ -38,10 +38,10 @@ code={`function requestRandomNumberWithCustomGas(
   // Calculate the fee for the custom gas limit
   uint256 fee = entropy.getFeeV2(customGasLimit);
 
-  // Request random number with custom gas limit
-  uint64 sequenceNumber = entropy.requestV2{ value: fee }(customGasLimit);
+// Request random number with custom gas limit
+uint64 sequenceNumber = entropy.requestV2{ value: fee }(customGasLimit);
 
-  // Store the sequence number for tracking if needed
+// Store the sequence number for tracking if needed
 }
 `} />
 
@@ -67,43 +67,45 @@ import { IEntropyConsumer } from "@pythnetwork/entropy-sdk-solidity/IEntropyCons
 import { IEntropyV2 } from "@pythnetwork/entropy-sdk-solidity/IEntropyV2.sol";
 
 contract CustomGasLimitExample is IEntropyConsumer {
-  IEntropyV2 public entropy;
-  mapping(uint64 => bool) public processedRequests;
+IEntropyV2 public entropy;
+mapping(uint64 => bool) public processedRequests;
 
-  constructor(address entropyAddress) {
-    entropy = IEntropyV2(entropyAddress);
-  }
+constructor(address entropyAddress) {
+entropy = IEntropyV2(entropyAddress);
+}
 
-  // Request with custom gas limit for complex callback
-  function requestComplexRandomNumber() external payable {
-    uint32 customGasLimit = 200000; // Higher limit for complex operations
-    uint256 fee = entropy.getFeeV2(customGasLimit);
+// Request with custom gas limit for complex callback
+function requestComplexRandomNumber() external payable {
+uint32 customGasLimit = 200000; // Higher limit for complex operations
+uint256 fee = entropy.getFeeV2(customGasLimit);
 
     require(msg.value >= fee, "Insufficient fee");
 
     uint64 sequenceNumber = entropy.requestV2{ value: fee }(customGasLimit);
     // Store sequence number if needed for tracking
-  }
 
-  // Request with lower gas limit for simple callback
-  function requestSimpleRandomNumber() external payable {
-    uint32 customGasLimit = 50000; // Lower limit for simple operations
-    uint256 fee = entropy.getFeeV2(customGasLimit);
+}
+
+// Request with lower gas limit for simple callback
+function requestSimpleRandomNumber() external payable {
+uint32 customGasLimit = 50000; // Lower limit for simple operations
+uint256 fee = entropy.getFeeV2(customGasLimit);
 
     require(msg.value >= fee, "Insufficient fee");
 
     uint64 sequenceNumber = entropy.requestV2{ value: fee }(customGasLimit);
-  }
-
-  // Complex callback that requires more gas
-  function entropyCallback(
-    uint64 sequenceNumber,
-    address provider,
-    bytes32 randomNumber
-  ) internal override {
-    // Prevent duplicate processing
-    require(!processedRequests[sequenceNumber], "Already processed");
-    processedRequests[sequenceNumber] = true;
+
+}
+
+// Complex callback that requires more gas
+function entropyCallback(
+uint64 sequenceNumber,
+address provider,
+bytes32 randomNumber
+) internal override {
+// Prevent duplicate processing
+require(!processedRequests[sequenceNumber], "Already processed");
+processedRequests[sequenceNumber] = true;
 
     // Complex operations that require more gas
     for (uint i = 0; i < 10; i++) {
@@ -114,11 +116,12 @@ contract CustomGasLimitExample is IEntropyConsumer {
     // Use the random number for your application logic
     uint256 randomValue = uint256(randomNumber);
     // Your application logic here...
-  }
 
-  function getEntropy() internal view override returns (address) {
-    return address(entropy);
-  }
+}
+
+function getEntropy() internal view override returns (address) {
+return address(entropy);
+}
 }
 `} />
 
@@ -127,10 +130,10 @@ contract CustomGasLimitExample is IEntropyConsumer {
 When setting custom gas limits, be aware of these constraints:
 
 <Callout variant="info" header="Gas Limit Rules">
-Gas limits are automatically rounded up to the nearest multiple of **10,000**. 
-Example: 19,000 becomes 20,000 25,500 becomes 30,000. The minimum gas limit
-is the provider's configured default limit. The maximum gas limit is
-655,350,000 (`uint16.max` \* 10,000).
+  Gas limits are automatically rounded up to the nearest multiple of **10,000**.
+  Example: 19,000 becomes 20,000 25,500 becomes 30,000. The minimum gas limit is
+  the provider's configured default limit. The maximum gas limit is 655,350,000
+  (`uint16.max` \* 10,000).
 </Callout>
 
 ### Recommended Gas Limits
@@ -146,44 +149,50 @@ is the provider's configured default limit. The maximum gas limit is
 
 Test your callback function to determine the actual gas usage:
 
-<DynamicCodeBlock lang="solidity" 
-code={`// In your tests, measure gas usage
+<DynamicCodeBlock
+  lang="solidity"
+  code={`// In your tests, measure gas usage
 uint256 gasStart = gasleft();
 // Your callback logic here
 uint256 gasUsed = gasStart - gasleft();
 console.log("Gas used:", gasUsed);
-`} />
+`}
+/>
 
 ### 2. Add Safety Buffer
 
 Always add a safety buffer to your estimated gas usage:
 
-<DynamicCodeBlock lang="solidity" 
-code={`uint32 estimatedGas = 150000;
+<DynamicCodeBlock
+  lang="solidity"
+  code={`uint32 estimatedGas = 150000;
 uint32 safetyBuffer = 20000;
 uint32 customGasLimit = estimatedGas + safetyBuffer;
-`} />
+`}
+/>
 
 ### 3. Handle Gas Limit Errors
 
 Be prepared to handle cases where your gas limit is insufficient:
 
 <Callout variant="warning">
-If your callback **runs out of gas**, the entropy provider will **not** be able to
-complete the callback. Always test your gas limits thoroughly and include
-adequate safety margins.
+  If your callback **runs out of gas**, the entropy provider will **not** be
+  able to complete the callback. Always test your gas limits thoroughly and
+  include adequate safety margins.
 </Callout>
 
 ### 4. Consider Fee Implications
 
 Higher gas limits result in higher fees. Balance your gas needs with cost considerations:
 
-<DynamicCodeBlock lang="solidity" 
-code={`// Compare fees for different gas limits
+<DynamicCodeBlock
+  lang="solidity"
+  code={`// Compare fees for different gas limits
 uint256 defaultFee = entropy.getFeeV2();
 uint256 customFee = entropy.getFeeV2(customGasLimit);
 uint256 additionalCost = customFee - defaultFee;
-`} />
+`}
+/>
 
 ## Troubleshooting
 

+ 2 - 1
apps/developer-hub/content/docs/entropy/whats-new-entropyv2.mdx

@@ -3,7 +3,8 @@ title: What's New in Entropy v2
 description: New features and improvements in Entropy v2
 icon: Sparkle
 ---
-import { DynamicCodeBlock } from 'fumadocs-ui/components/dynamic-codeblock';
+
+import { DynamicCodeBlock } from "fumadocs-ui/components/dynamic-codeblock";
 
 ## Key Improvements
 

+ 1 - 0
apps/developer-hub/package.json

@@ -33,6 +33,7 @@
     "react": "catalog:",
     "react-aria": "catalog:",
     "react-dom": "catalog:",
+    "viem": "catalog:",
     "zod": "catalog:",
     "zod-validation-error": "catalog:"
   },

+ 1 - 5
apps/developer-hub/src/app/(docs)/layout.tsx

@@ -4,9 +4,5 @@ import type { ReactNode } from "react";
 import { docsOptions } from "../../config/layout.config";
 
 export default function Layout({ children }: { children: ReactNode }) {
-  return (
-    <DocsLayout {...docsOptions}>
-      {children}
-    </DocsLayout>
-  );
+  return <DocsLayout {...docsOptions}>{children}</DocsLayout>;
 }

+ 1 - 5
apps/developer-hub/src/app/(homepage)/layout.tsx

@@ -4,9 +4,5 @@ import type { ReactNode } from "react";
 import { baseOptions } from "../../config/layout.config";
 
 export default function Layout({ children }: { children: ReactNode }) {
-  return (
-    <HomeLayout {...baseOptions}>
-      {children}
-    </HomeLayout>
-  );
+  return <HomeLayout {...baseOptions}>{children}</HomeLayout>;
 }

+ 2 - 5
apps/developer-hub/src/components/CopyAddress/index.tsx

@@ -3,21 +3,18 @@
 import { CopyButton } from "@pythnetwork/component-library/CopyButton";
 import { Link } from "@pythnetwork/component-library/Link";
 
-import TruncateToMiddle from "../TruncateToMiddle";
 import styles from "./index.module.scss";
 
 const CopyAddress = ({ address, url }: { address: string; url?: string }) => {
   return url ? (
     <div className={styles.address}>
       <Link href={url} target="_blank" rel="noreferrer">
-        <TruncateToMiddle text={address} />
+        {address}
       </Link>
       <CopyButton text={address} iconOnly />
     </div>
   ) : (
-    <CopyButton text={address}>
-      <TruncateToMiddle text={address} />
-    </CopyButton>
+    <CopyButton text={address}>{address}</CopyButton>
   );
 };
 

+ 4 - 0
apps/developer-hub/src/components/EntropyTable/constants.ts

@@ -0,0 +1,4 @@
+export const FORTUNA_API_URLS = {
+  mainnet: "https://fortuna.dourolabs.app/v1/chains/configs",
+  testnet: "https://fortuna-staging.dourolabs.app/v1/chains/configs",
+} as const;

+ 72 - 0
apps/developer-hub/src/components/EntropyTable/entropy-api-data-fetcher.tsx

@@ -0,0 +1,72 @@
+import * as chains from "viem/chains";
+import { z } from "zod";
+
+import { EntropyDeploymentsConfig } from "./entropy-deployments-config";
+
+const ApiChainConfigSchema = z.object({
+  name: z.string(),
+  network_id: z.number(),
+  contract_addr: z.string(),
+  reveal_delay_blocks: z.number(),
+  gas_limit: z.number(),
+  default_fee: z.number(),
+});
+
+type ApiChainConfig = z.infer<typeof ApiChainConfigSchema>;
+
+const entropyDeploymentsSchema = z.array(ApiChainConfigSchema);
+
+export type EntropyDeployment = {
+  address: string;
+  delay: string;
+  gasLimit: string;
+  default_fee: number;
+  rpc?: string;
+  explorer?: string;
+  nativeCurrency?: string;
+};
+
+const getChainData = (network_id: number) => {
+  return Object.values(chains).find((chain) => chain.id === network_id);
+};
+
+const transformChainData = (
+  chain: ApiChainConfig,
+): [string, EntropyDeployment] => {
+  const viemChainData = getChainData(chain.network_id);
+
+  const configOverride = EntropyDeploymentsConfig[chain.network_id];
+
+  const rpc = configOverride?.rpc ?? viemChainData?.rpcUrls.default.http[0];
+  const explorer =
+    configOverride?.explorer ?? viemChainData?.blockExplorers?.default.url;
+  const nativeCurrency =
+    configOverride?.nativeCurrency ?? viemChainData?.nativeCurrency.symbol;
+
+  const deployment: EntropyDeployment = {
+    address: chain.contract_addr,
+    delay: `${String(chain.reveal_delay_blocks)} block${
+      chain.reveal_delay_blocks === 1 ? "" : "s"
+    }`,
+    gasLimit: String(chain.gas_limit),
+    default_fee: chain.default_fee,
+    ...(rpc ? { rpc } : {}),
+    ...(explorer ? { explorer } : {}),
+    ...(nativeCurrency ? { nativeCurrency } : {}),
+  };
+
+  return [chain.name, deployment];
+};
+
+export const fetchEntropyDeployments = async (
+  url: string,
+): Promise<Record<string, EntropyDeployment>> => {
+  try {
+    const response = await fetch(url);
+    const apiData = entropyDeploymentsSchema.parse(await response.json());
+
+    return Object.fromEntries(apiData.map((item) => transformChainData(item)));
+  } catch (error_) {
+    throw new Error(String(error_));
+  }
+};

+ 25 - 0
apps/developer-hub/src/components/EntropyTable/entropy-deployments-config.ts

@@ -0,0 +1,25 @@
+export type ChainOverride = {
+  rpc?: string;
+  nativeCurrency?: string;
+  explorer?: string;
+};
+
+export const EntropyDeploymentsConfig: Record<string, ChainOverride> = {
+  // Example overrides - add your custom configurations here
+  // "network-id": {
+  //   rpc: "https://custom-rpc-url.com",
+  //   nativeCurrency: "CUSTOM"
+  //   explorer: "https://custom-explorer.com"
+  // },
+
+  // Override examples (uncomment and modify as needed):
+  "998": {
+    rpc: "https://rpc.hyperliquid-testnet.xyz/evm",
+    nativeCurrency: "HYPE",
+    explorer: "https://testnet.purrsec.com/",
+  },
+  "999": {
+    rpc: "https://rpc.hypurrscan.io",
+    nativeCurrency: "HYPE",
+  },
+};

+ 125 - 0
apps/developer-hub/src/components/EntropyTable/index.tsx

@@ -0,0 +1,125 @@
+"use client";
+import { InfoBox } from "@pythnetwork/component-library/InfoBox";
+import { Spinner } from "@pythnetwork/component-library/Spinner";
+import type {
+  ColumnConfig,
+  RowConfig,
+} from "@pythnetwork/component-library/Table";
+import { Table } from "@pythnetwork/component-library/Table";
+import { useEffect, useRef, useState } from "react";
+
+import { FORTUNA_API_URLS } from "./constants";
+import type { EntropyDeployment } from "./entropy-api-data-fetcher";
+import { fetchEntropyDeployments } from "./entropy-api-data-fetcher";
+import CopyAddress from "../CopyAddress";
+
+export const EntropyTable = ({ isMainnet }: { isMainnet: boolean }) => {
+  const isLoading = useRef(false);
+  const [state, setState] = useState<State>(State.NotLoaded());
+
+  useEffect(() => {
+    if (!isLoading.current) {
+      setState(State.Loading());
+      isLoading.current = true;
+      getEntropyDeployments(isMainnet)
+        .then((chains) => {
+          setState(State.Loaded(chains));
+        })
+        .catch((error: unknown) => {
+          setState(State.Failed(error));
+        });
+    }
+  }, [isMainnet]);
+
+  switch (state.type) {
+    case StateType.Loading:
+    case StateType.NotLoaded: {
+      return <Spinner label="Fetching the list of entropy contracts..." />;
+    }
+    case StateType.Error: {
+      return (
+        <InfoBox title="Error" variant="error">
+          <p>Failed to fetch the list of entropy contracts.</p>
+        </InfoBox>
+      );
+    }
+    case StateType.Loaded: {
+      return <EntropyTableContent chains={state.chains} />;
+    }
+    default: {
+      throw new Error(`Unknown state type: ${typeof state}`);
+    }
+  }
+};
+
+type Col = "chain" | "address" | "delay" | "gasLimit";
+
+const EntropyTableContent = ({
+  chains,
+}: {
+  chains: Record<string, EntropyDeployment>;
+}) => {
+  const columns: ColumnConfig<Col>[] = [
+    { id: "chain", name: "Chain", isRowHeader: true },
+    { id: "address", name: "Contract" },
+    { id: "delay", name: "Reveal Delay" },
+    { id: "gasLimit", name: "Default Gas Limit" },
+  ];
+
+  const rows: RowConfig<Col>[] = Object.entries(chains).map(
+    ([chainName, d]) => ({
+      id: chainName,
+      data: {
+        chain: chainName,
+        address: d.explorer ? (
+          <CopyAddress
+            address={d.address}
+            url={`${d.explorer}/address/${d.address}`}
+          />
+        ) : (
+          <CopyAddress address={d.address} />
+        ),
+        delay: d.delay,
+        gasLimit: d.gasLimit,
+      },
+    }),
+  );
+
+  return (
+    <Table<Col>
+      label="Entropy deployments"
+      columns={columns}
+      rows={rows}
+      isLoading={false}
+      rounded
+      fill
+      stickyHeader="top"
+    />
+  );
+};
+
+enum StateType {
+  NotLoaded,
+  Loading,
+  Loaded,
+  Error,
+}
+
+const State = {
+  NotLoaded: () => ({ type: StateType.NotLoaded as const }),
+  Loading: () => ({ type: StateType.Loading as const }),
+  Loaded: (chains: Awaited<ReturnType<typeof getEntropyDeployments>>) => ({
+    type: StateType.Loaded as const,
+    chains,
+  }),
+  Failed: (error: unknown) => ({ type: StateType.Error as const, error }),
+};
+
+type State = ReturnType<(typeof State)[keyof typeof State]>;
+
+const getEntropyDeployments = async (
+  isMainnet: boolean,
+): Promise<Record<string, EntropyDeployment>> => {
+  const url = isMainnet ? FORTUNA_API_URLS.mainnet : FORTUNA_API_URLS.testnet;
+  return await fetchEntropyDeployments(url);
+};

+ 3 - 0
pnpm-lock.yaml

@@ -548,6 +548,9 @@ importers:
       react-dom:
         specifier: 'catalog:'
         version: 19.1.0(react@19.1.0)
+      viem:
+        specifier: 'catalog:'
+        version: 2.34.0(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@6.0.3)(zod@3.24.4)
       zod:
         specifier: 'catalog:'
         version: 3.24.4