diff --git a/components/tools/common/api/validator-info.ts b/components/tools/common/api/validator-info.ts
index d5ece29657c..2998d6124fb 100644
--- a/components/tools/common/api/validator-info.ts
+++ b/components/tools/common/api/validator-info.ts
@@ -1,7 +1,11 @@
import { Validator, SubnetInfo, L1ValidatorManagerDetails } from './types';
import { pChainEndpoint } from './consts';
import { AvaCloudSDK } from "@avalabs/avacloud-sdk";
+import { useWalletStore } from "../../../../toolbox/src/stores/walletStore";
+const { isTestnet } = useWalletStore();
+
export const avaCloudSDK = new AvaCloudSDK({
+ serverURL: isTestnet ? "https://api.avax-test.network" : "https://api.avax.network",
chainId: "43114",
network: "fuji",
});
diff --git a/package.json b/package.json
index e3bccef8ebe..4d46682e937 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,7 @@
"postinstall": "prisma generate && fumadocs-mdx && cd toolbox && yarn"
},
"dependencies": {
- "@avalabs/avacloud-sdk": "0.8.7",
+ "@avalabs/avacloud-sdk": "0.12.1",
"@avalabs/avalanchejs": "^5.0.0",
"@fumadocs/mdx-remote": "^1.2.0",
"@hookform/resolvers": "^4.1.3",
@@ -129,4 +129,4 @@
"typescript": "^5.8.2"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
-}
+}
\ No newline at end of file
diff --git a/toolbox/package.json b/toolbox/package.json
index f300ad8f0cc..95ceb445064 100644
--- a/toolbox/package.json
+++ b/toolbox/package.json
@@ -26,7 +26,7 @@
"postinstall": "node update_docker_tags.js"
},
"dependencies": {
- "@avalabs/avacloud-sdk": "^0.9.0",
+ "@avalabs/avacloud-sdk": "^0.12.1",
"@avalabs/avalanchejs": "^4.1.2-alpha.4",
"@radix-ui/react-alert-dialog": "^1.1.13",
"@radix-ui/react-checkbox": "^1.2.3",
@@ -77,4 +77,4 @@
"vite": "^6.1.6",
"vite-plugin-dts": "^4.5.0"
}
-}
+}
\ No newline at end of file
diff --git a/toolbox/src/components/BlockchainDetailsDisplay.tsx b/toolbox/src/components/BlockchainDetailsDisplay.tsx
new file mode 100644
index 00000000000..eb7d14a0a69
--- /dev/null
+++ b/toolbox/src/components/BlockchainDetailsDisplay.tsx
@@ -0,0 +1,283 @@
+"use client"
+
+import { Calendar, Users, Database, Key, Copy, AlertTriangle, FileText, Globe } from "lucide-react"
+import { useState } from "react"
+import { Subnet } from "@avalabs/avacloud-sdk/models/components";
+import type { BlockchainInfo } from "./SelectBlockchain";
+import { SUBNET_EVM_VM_ID } from "../toolbox/Nodes/AvalanchegoDocker";
+import { useWalletStore } from "../stores/walletStore";
+
+interface BlockchainDetailsDisplayProps {
+ type: 'blockchain' | 'subnet'
+ data: BlockchainInfo | Subnet | null
+ isLoading?: boolean
+}
+
+export default function BlockchainDetailsDisplay({ type, data, isLoading }: BlockchainDetailsDisplayProps) {
+ const [copiedText, setCopiedText] = useState(null)
+ const { isTestnet } = useWalletStore();
+
+ if (isLoading) {
+ return (
+
+
+
+
+
+ Loading {type} details...
+
+
+
+
+ )
+ }
+
+ if (!data) {
+ return null
+ }
+
+ const formatTimestamp = (timestamp: number) => {
+ const date = new Date(timestamp * 1000)
+ return date.toLocaleDateString("en-US", {
+ year: "2-digit",
+ month: "short",
+ day: "numeric",
+ })
+ }
+
+ const copyToClipboard = (text: string | number | undefined | null) => {
+ if (!text) return
+ navigator.clipboard.writeText(text.toString())
+ setCopiedText(text.toString())
+ setTimeout(() => setCopiedText(null), 2000)
+ }
+
+ // Determine if we're showing subnet or blockchain
+ const isSubnet = type === 'subnet'
+ const subnet = isSubnet ? (data as Subnet) : null
+
+ // Get blockchain data - either directly or from subnet's first blockchain
+ const blockchain = isSubnet
+ ? (subnet?.blockchains?.[0] ? { ...(subnet.blockchains[0] as any), isTestnet } : null)
+ : (data as BlockchainInfo)
+
+ return (
+
+ {/* Header */}
+
+
+
+ {isSubnet ? 'Subnet Details' : 'Blockchain Details'}
+
+
+ {isSubnet && subnet?.isL1 && (
+
+ L1 Chain
+
+ )}
+ {blockchain?.isTestnet !== undefined && (
+
+ {blockchain.isTestnet ? "Testnet" : "Mainnet"}
+
+ )}
+
+
+
+
+ {/* Content Area */}
+
+ {/* Subnet-specific information */}
+ {isSubnet && subnet && (
+ <>
+ {/* Basic Subnet Information */}
+
+
+
+
+ Created:
+ {formatTimestamp(subnet.createBlockTimestamp)}
+
+
+
+ Chains:
+ {subnet.blockchains?.length || 0}
+
+
+
+ {subnet.subnetOwnershipInfo.addresses && subnet.subnetOwnershipInfo.addresses.length > 0 && (
+
+
+
+
Owner:
+
+
+ {subnet.subnetOwnershipInfo.addresses[0]}
+
+
+
+
+
+ {subnet.subnetOwnershipInfo.threshold} of {subnet.subnetOwnershipInfo.addresses.length} signatures required
+
+
+ )}
+
+
+
+ {/* L1 Conversion Information */}
+ {subnet.isL1 && subnet.l1ValidatorManagerDetails && (
+
+
+
+ L1 Conversion
+
+
+
+
Validator Manager:
+
+
+ {subnet.l1ValidatorManagerDetails?.contractAddress}
+
+
+
+
+
+
+ )}
+ >
+ )}
+
+ {/* Blockchain Information */}
+ {blockchain && (
+
+
+
+
+ {isSubnet ? 'Blockchain Details' : 'Details'}
+
+
+
+ {/* Basic blockchain info for blockchain-only view */}
+ {!isSubnet && (
+
+
+
+
+ Created:
+ {formatTimestamp(blockchain.createBlockTimestamp)}
+
+
+
+ Name:
+ {blockchain.blockchainName || "Unknown"}
+
+
+
+
+
+ Create Block:
+ {blockchain.createBlockNumber}
+
+
+
+ )}
+
+
+
+
+
EVM Chain ID:
+
+
+ {blockchain.evmChainId}
+
+
+
+
+
+
+
Blockchain ID:
+
+
+ {blockchain.blockchainId}
+
+
+
+
+
+
+
Subnet ID:
+
+
+ {blockchain.subnetId}
+
+
+
+
+
+
+
VM ID:
+
+
+ {blockchain.vmId}
+
+
+
+
+
+
+ {/* Warning for non-standard VM */}
+ {blockchain.vmId && blockchain.vmId !== SUBNET_EVM_VM_ID && (
+
+
+
Non-standard VM ID detected
+
+ )}
+
+
+ )}
+
+ {/* Copy feedback */}
+ {copiedText && (
+
+ Copied to clipboard!
+
+ )}
+
+
+ )
+}
\ No newline at end of file
diff --git a/toolbox/src/components/InputSubnetId.tsx b/toolbox/src/components/InputSubnetId.tsx
new file mode 100644
index 00000000000..e3fa64992fc
--- /dev/null
+++ b/toolbox/src/components/InputSubnetId.tsx
@@ -0,0 +1,142 @@
+"use client"
+
+import { Input, type Suggestion } from "./Input";
+import { useL1ListStore } from "../stores/l1ListStore";
+import { useCreateChainStore } from "../stores/createChainStore";
+import { useMemo, useState, useCallback, useEffect } from "react";
+import { utils } from "@avalabs/avalanchejs";
+import { useAvaCloudSDK } from "../stores/useAvaCloudSDK";
+
+// Primary network subnet ID
+const PRIMARY_NETWORK_SUBNET_ID = "11111111111111111111111111111111LpoYY";
+
+export default function InputSubnetId({
+ value,
+ onChange,
+ error,
+ label = "Subnet ID",
+ hidePrimaryNetwork = false,
+ helperText,
+ id,
+ validationDelayMs = 500,
+ readOnly = false
+}: {
+ value: string,
+ onChange: (value: string) => void,
+ error?: string | null,
+ label?: string,
+ hidePrimaryNetwork?: boolean
+ helperText?: string | null
+ id?: string
+ validationDelayMs?: number
+ readOnly?: boolean
+}) {
+ const createChainStoreSubnetId = useCreateChainStore()(state => state.subnetId);
+ const { l1List } = useL1ListStore()();
+ const { getSubnetById } = useAvaCloudSDK();
+
+ const [validationError, setValidationError] = useState(null);
+
+ // Validate subnet ID format and checksum using Base58Check
+ const validateBase58Format = (subnetId: string): boolean => {
+ try {
+ // Validate Base58Check format and checksum (last 4 bytes)
+ utils.base58check.decode(subnetId);
+ return true;
+ } catch (error) {
+ return false;
+ }
+ };
+
+ // Validate subnet ID using AvaCloud SDK
+ const validateSubnetId = useCallback(async (subnetId: string) => {
+ if (!subnetId || subnetId.length < 10) {
+ setValidationError(null);
+ return;
+ }
+
+ // First validate Base58Check format and checksum
+ if (!validateBase58Format(subnetId)) {
+ setValidationError("Invalid subnet ID format or checksum");
+ return;
+ }
+
+ try {
+ setValidationError(null);
+
+ await getSubnetById({ subnetId });
+
+ // If we get here, the subnet exists
+ setValidationError(null);
+ } catch (error) {
+ // Show validation error for invalid subnet IDs
+ setValidationError("Subnet ID not found or invalid");
+ }
+ }, [getSubnetById]);
+
+ // Validate when value changes
+ useEffect(() => {
+ const timeoutId = setTimeout(() => {
+ if (value) {
+ validateSubnetId(value);
+ } else {
+ setValidationError(null);
+ }
+ }, validationDelayMs); // Wait for subnet to be available before validation
+
+ return () => clearTimeout(timeoutId);
+ }, [value, validateSubnetId, validationDelayMs]);
+
+ const subnetIdSuggestions: Suggestion[] = useMemo(() => {
+ const result: Suggestion[] = [];
+ const seen = new Set();
+
+ // Add subnet from create chain store first
+ if (createChainStoreSubnetId && !(hidePrimaryNetwork && createChainStoreSubnetId === PRIMARY_NETWORK_SUBNET_ID)) {
+ result.push({
+ title: createChainStoreSubnetId,
+ value: createChainStoreSubnetId,
+ description: "The Subnet that you have just created in the \"Create Chain\" tool"
+ });
+ seen.add(createChainStoreSubnetId);
+ }
+
+ // Add subnets from L1 list
+ for (const l1 of l1List) {
+ const { subnetId, name } = l1;
+
+ if (!subnetId || seen.has(subnetId)) continue;
+
+ if (hidePrimaryNetwork && subnetId === PRIMARY_NETWORK_SUBNET_ID) {
+ continue;
+ }
+
+ result.push({
+ title: `${name} (${subnetId})`,
+ value: subnetId,
+ description: l1.description || "A subnet that was added to your L1 list.",
+ });
+
+ seen.add(subnetId);
+ }
+
+ return result;
+ }, [createChainStoreSubnetId, l1List, hidePrimaryNetwork]);
+
+ // Combine validation error with passed error
+ const combinedError = error || validationError;
+
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/toolbox/src/components/SelectBlockchain.tsx b/toolbox/src/components/SelectBlockchain.tsx
new file mode 100644
index 00000000000..e91b35e8c1a
--- /dev/null
+++ b/toolbox/src/components/SelectBlockchain.tsx
@@ -0,0 +1,121 @@
+"use client"
+
+import SelectBlockchainId from "./SelectBlockchainId";
+import { useState, useCallback } from "react";
+import { useWalletStore } from "../stores/walletStore";
+import { networkIDs } from "@avalabs/avalanchejs";
+
+// API Response type from AvaCloud - matches the official API response
+export type BlockchainApiResponse = {
+ createBlockTimestamp: number;
+ createBlockNumber: string;
+ blockchainId: string;
+ vmId: string;
+ subnetId: string;
+ blockchainName: string;
+ evmChainId: number;
+}
+
+// Extended type with additional metadata
+export type BlockchainInfo = BlockchainApiResponse & {
+ isTestnet: boolean;
+}
+
+export type BlockchainSelection = {
+ blockchainId: string;
+ blockchain: BlockchainInfo | null;
+}
+
+// Import the unified details display component
+import BlockchainDetailsDisplay from "./BlockchainDetailsDisplay";
+
+export default function SelectBlockchain({
+ value,
+ onChange,
+ error,
+ label = "Select Avalanche Blockchain ID"
+}: {
+ value: string,
+ onChange: (selection: BlockchainSelection) => void,
+ error?: string | null,
+ label?: string
+}) {
+ const { avalancheNetworkID } = useWalletStore();
+ const [blockchainDetails, setBlockchainDetails] = useState>({});
+ const [isLoading, setIsLoading] = useState(false);
+
+ // Network names for API calls
+ const networkNames: Record = {
+ [networkIDs.MainnetID]: "mainnet",
+ [networkIDs.FujiID]: "fuji",
+ };
+
+ // Fetch blockchain details when needed
+ const fetchBlockchainDetails = useCallback(async (blockchainId: string) => {
+ if (!blockchainId || blockchainDetails[blockchainId]) return;
+
+ try {
+ const network = networkNames[Number(avalancheNetworkID)];
+ if (!network) return;
+
+ setIsLoading(true);
+
+ // Use direct API call as shown in AvaCloud documentation
+ // https://developers.avacloud.io/data-api/primary-network/get-blockchain-details-by-id
+ const response = await fetch(`https://glacier-api.avax.network/v1/networks/${network}/blockchains/${blockchainId}`, {
+ method: 'GET',
+ headers: {
+ 'accept': 'application/json',
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error(`Failed to fetch blockchain details: ${response.statusText}`);
+ }
+
+ const blockchain: BlockchainApiResponse = await response.json();
+
+ setBlockchainDetails(prev => ({
+ ...prev,
+ [blockchainId]: {
+ ...blockchain,
+ isTestnet: network === "fuji"
+ }
+ }));
+ } catch (error) {
+ console.error(`Error fetching blockchain details for ${blockchainId}:`, error);
+ } finally {
+ setIsLoading(false);
+ }
+ }, [avalancheNetworkID, networkNames, blockchainDetails]);
+
+ // Handle value change and fetch details if needed
+ const handleValueChange = useCallback((newValue: string) => {
+ if (newValue && !blockchainDetails[newValue]) {
+ fetchBlockchainDetails(newValue);
+ }
+
+ onChange({
+ blockchainId: newValue,
+ blockchain: blockchainDetails[newValue] || null
+ });
+ }, [fetchBlockchainDetails, blockchainDetails, onChange]);
+
+ // Get current blockchain details for display
+ const currentBlockchain = value ? blockchainDetails[value] || null : null;
+ const isLoadingCurrent = value && !blockchainDetails[value] && isLoading;
+
+ return (
+
+
+
+ {/* Display blockchain details when a blockchain is selected */}
+ {value && }
+
+ );
+}
\ No newline at end of file
diff --git a/toolbox/src/components/SelectChainID.tsx b/toolbox/src/components/SelectBlockchainId.tsx
similarity index 96%
rename from toolbox/src/components/SelectChainID.tsx
rename to toolbox/src/components/SelectBlockchainId.tsx
index 33e49960fee..2535a2a762d 100644
--- a/toolbox/src/components/SelectChainID.tsx
+++ b/toolbox/src/components/SelectBlockchainId.tsx
@@ -5,14 +5,14 @@ import { useMemo } from "react";
import { cn } from "../lib/utils";
import { Globe } from 'lucide-react';
-interface ChainOption {
+interface BlockchainOption {
id: string;
name: string;
description: string;
logoUrl?: string;
}
-export default function SelectChainID({
+export default function SelectBlockchainId({
value,
onChange,
error,
@@ -28,8 +28,8 @@ export default function SelectChainID({
const { l1List } = useL1ListStore()();
const selectId = useId();
- const options: ChainOption[] = useMemo(() => {
- const result: ChainOption[] = [];
+ const options: BlockchainOption[] = useMemo(() => {
+ const result: BlockchainOption[] = [];
if (createChainStorechainID) {
result.push({
@@ -90,7 +90,7 @@ export default function SelectChainID({
) : (
- Select a chain ID
+ Select a blockchain ID
)}
@@ -129,4 +129,4 @@ export default function SelectChainID({
{error && {error}
}
);
-}
+}
\ No newline at end of file
diff --git a/toolbox/src/components/SelectSubnet.tsx b/toolbox/src/components/SelectSubnet.tsx
new file mode 100644
index 00000000000..4936b80eba6
--- /dev/null
+++ b/toolbox/src/components/SelectSubnet.tsx
@@ -0,0 +1,96 @@
+"use client"
+
+import SelectSubnetId from "./SelectSubnetId";
+import { useState, useCallback, useEffect } from "react";
+import { Subnet } from "@avalabs/avacloud-sdk/models/components";
+import BlockchainDetailsDisplay from "./BlockchainDetailsDisplay";
+import { useAvaCloudSDK } from "../stores/useAvaCloudSDK";
+
+export type SubnetSelection = {
+ subnetId: string;
+ subnet: Subnet | null;
+}
+
+export default function SelectSubnet({
+ value,
+ onChange,
+ error,
+ onlyNotConverted = false,
+ hidePrimaryNetwork = false
+}: {
+ value: string,
+ onChange: (selection: SubnetSelection) => void,
+ error?: string | null,
+ onlyNotConverted?: boolean,
+ hidePrimaryNetwork?: boolean
+}) {
+ const { getSubnetById } = useAvaCloudSDK();
+ const [subnetDetails, setSubnetDetails] = useState>({});
+ const [isLoading, setIsLoading] = useState(false);
+
+ // Fetch subnet details when needed
+ const fetchSubnetDetails = useCallback(async (subnetId: string) => {
+ if (!subnetId || subnetDetails[subnetId]) return;
+
+ try {
+ setIsLoading(true);
+ const subnet = await getSubnetById({ subnetId });
+
+ setSubnetDetails(prev => ({
+ ...prev,
+ [subnetId]: subnet
+ }));
+
+ // Automatically update the selection with the fetched subnet details
+ onChange({
+ subnetId,
+ subnet
+ });
+ } catch (error) {
+ console.error(`Error fetching subnet details for ${subnetId}:`, error);
+ } finally {
+ setIsLoading(false);
+ }
+ }, [getSubnetById, subnetDetails, onChange]);
+
+ // Handle value change and fetch details if needed
+ const handleValueChange = useCallback((newValue: string) => {
+ if (newValue && !subnetDetails[newValue]) {
+ fetchSubnetDetails(newValue);
+ }
+
+ onChange({
+ subnetId: newValue,
+ subnet: subnetDetails[newValue] || null
+ });
+ }, [fetchSubnetDetails, subnetDetails, onChange]);
+
+ // Auto-fetch subnet details when component receives a pre-filled value
+ useEffect(() => {
+ if (value && !subnetDetails[value]) {
+ fetchSubnetDetails(value);
+ }
+ }, [value, subnetDetails, fetchSubnetDetails]);
+
+ // Get current subnet details for display
+ const currentSubnet = value ? subnetDetails[value] || null : null;
+ const isLoadingCurrent = value && !subnetDetails[value] && isLoading;
+
+ return (
+
+
+
+ {/* Display subnet details when a subnet is selected */}
+ {value && }
+
+ );
+}
\ No newline at end of file
diff --git a/toolbox/src/components/SelectSubnetId.tsx b/toolbox/src/components/SelectSubnetId.tsx
index 948bd742cf6..37720a3cd74 100644
--- a/toolbox/src/components/SelectSubnetId.tsx
+++ b/toolbox/src/components/SelectSubnetId.tsx
@@ -1,17 +1,45 @@
+import InputSubnetId from "./InputSubnetId";
import { Input, type Suggestion } from "./Input";
import { useCreateChainStore } from "../stores/createChainStore";
import { useL1ListStore } from "../stores/l1ListStore";
import { useMemo } from "react";
-export default function InputSubnetId({ value, onChange, error, onlyNotConverted = false, hidePrimaryNetwork = false }: { value: string, onChange: (value: string) => void, error?: string | null, onlyNotConverted?: boolean, hidePrimaryNetwork?: boolean }) {
+export default function SelectSubnetId({
+ value,
+ onChange,
+ error,
+ onlyNotConverted = false,
+ hidePrimaryNetwork = false,
+ label = "Subnet ID",
+ helperText,
+ id
+}: {
+ value: string,
+ onChange: (value: string) => void,
+ error?: string | null,
+ onlyNotConverted?: boolean,
+ hidePrimaryNetwork?: boolean,
+ label?: string,
+ helperText?: string | null,
+ id?: string
+}) {
+ // This component adds filtering logic on top of InputSubnetId
+ // If onlyNotConverted is true, it filters out converted subnets
+
const createChainStoreSubnetId = useCreateChainStore()(state => state.subnetId);
const l1List = useL1ListStore()(state => state.l1List);
- const subnetIdSuggestions: Suggestion[] = useMemo(() => {
+ // Create filtered suggestions if onlyNotConverted is true
+ const filteredSuggestions: Suggestion[] | undefined = useMemo(() => {
+ if (!onlyNotConverted) {
+ // If no filtering needed, let InputSubnetId handle suggestions
+ return undefined;
+ }
+
const result: Suggestion[] = [];
const seen = new Set();
const PRIMARY_NETWORK_ID = "11111111111111111111111111111111LpoYY";
-
+
if (createChainStoreSubnetId) {
result.push({
title: createChainStoreSubnetId,
@@ -20,37 +48,57 @@ export default function InputSubnetId({ value, onChange, error, onlyNotConverted
});
seen.add(createChainStoreSubnetId);
}
-
+
for (const l1 of l1List) {
const { subnetId, name, validatorManagerAddress } = l1;
-
+
if (!subnetId || seen.has(subnetId)) continue;
-
+
const isPrimary = subnetId === PRIMARY_NETWORK_ID;
const isConverted = !!validatorManagerAddress;
-
+
+ // Filter out converted subnets when onlyNotConverted is true
if ((onlyNotConverted && (isPrimary || isConverted)) || (hidePrimaryNetwork && isPrimary)) {
continue;
}
-
+
result.push({
title: `${name} (${subnetId})`,
value: subnetId,
description: l1.description || 'A chain that was added to your L1 list.',
});
-
+
seen.add(subnetId);
}
-
+
return result;
}, [createChainStoreSubnetId, l1List, onlyNotConverted, hidePrimaryNetwork]);
-
-
- return
+
+ // If we need custom filtering, use Input directly with filtered suggestions
+ // Otherwise, use InputSubnetId which has its own suggestions and validation
+ if (onlyNotConverted && filteredSuggestions) {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
}
diff --git a/toolbox/src/components/SelectValidationID.tsx b/toolbox/src/components/SelectValidationID.tsx
index 92660e212d3..1bf2ddfbcd2 100644
--- a/toolbox/src/components/SelectValidationID.tsx
+++ b/toolbox/src/components/SelectValidationID.tsx
@@ -1,11 +1,9 @@
import { Input, type Suggestion } from "./Input";
import { useMemo, useState, useEffect } from "react";
import { cb58ToHex, hexToCB58 } from "../toolbox/Conversion/FormatConverter";
-import { AvaCloudSDK } from "@avalabs/avacloud-sdk";
-import { useWalletStore } from "../stores/walletStore";
-import { networkIDs } from "@avalabs/avalanchejs";
-import { L1ValidatorDetailsFull, GlobalParamNetwork } from "@avalabs/avacloud-sdk/models/components";
+import { L1ValidatorDetailsFull } from "@avalabs/avacloud-sdk/models/components";
import { formatAvaxBalance } from "../coreViem/utils/format";
+import { useAvaCloudSDK } from "../stores/useAvaCloudSDK";
export type ValidationSelection = {
validationId: string;
@@ -44,44 +42,34 @@ export type ValidationSelection = {
* @param props.subnetId - Optional subnet ID to filter validators
* @param props.format - Format for validation ID: "cb58" (default) or "hex"
*/
-export default function SelectValidationID({
- value,
- onChange,
+export default function SelectValidationID({
+ value,
+ onChange,
error,
subnetId = "",
- format = "cb58"
-}: {
- value: string,
- onChange: (selection: ValidationSelection) => void,
- error?: string | null,
+ format = "cb58"
+}: {
+ value: string,
+ onChange: (selection: ValidationSelection) => void,
+ error?: string | null,
subnetId?: string,
format?: "cb58" | "hex"
}) {
- const { avalancheNetworkID } = useWalletStore();
+ const { listL1Validators } = useAvaCloudSDK();
const [validators, setValidators] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [validationIdToNodeId, setValidationIdToNodeId] = useState>({});
- // Network names for display
- const networkNames: Record = {
- [networkIDs.MainnetID]: "mainnet",
- [networkIDs.FujiID]: "fuji",
- };
-
// Fetch validators from the API
useEffect(() => {
const fetchValidators = async () => {
if (!subnetId) return;
-
+
setIsLoading(true);
try {
- const network = networkNames[Number(avalancheNetworkID)];
- if (!network) return;
-
- const result = await new AvaCloudSDK().data.primaryNetwork.listL1Validators({
- network: network,
+ const result = await listL1Validators({
subnetId: subnetId,
- includeInactiveL1Validators: true,
+ pageSize: 100, // Add reasonable page size
});
// Handle pagination
@@ -89,7 +77,7 @@ export default function SelectValidationID({
for await (const page of result) {
validatorsList.push(...page.result.validators);
}
-
+
setValidators(validatorsList);
// Create a mapping of validation IDs to node IDs, filtering out validators with weight 0
@@ -115,14 +103,14 @@ export default function SelectValidationID({
};
fetchValidators();
- }, [subnetId, avalancheNetworkID]);
+ }, [subnetId, listL1Validators]);
// Get the currently selected node ID
const selectedNodeId = useMemo(() => {
- return validationIdToNodeId[value] ||
- (value && value.startsWith("0x") && validationIdToNodeId[value]) ||
- (value && !value.startsWith("0x") && validationIdToNodeId["0x" + cb58ToHex(value)]) ||
- "";
+ return validationIdToNodeId[value] ||
+ (value && value.startsWith("0x") && validationIdToNodeId[value]) ||
+ (value && !value.startsWith("0x") && validationIdToNodeId["0x" + cb58ToHex(value)]) ||
+ "";
}, [value, validationIdToNodeId]);
const validationIDSuggestions: Suggestion[] = useMemo(() => {
@@ -130,7 +118,7 @@ export default function SelectValidationID({
// Filter out validators with weight 0 and only add suggestions from validators with node IDs
const validatorsWithWeight = validators.filter(validator => validator.weight > 0);
-
+
for (const validator of validatorsWithWeight) {
if (validator.validationId) {
// Use full node ID
@@ -138,7 +126,7 @@ export default function SelectValidationID({
const weightDisplay = validator.weight.toLocaleString();
const balanceDisplay = formatAvaxBalance(validator.remainingBalance);
const isSelected = nodeId === selectedNodeId;
-
+
// Add just one version based on the format prop
if (format === "hex") {
try {
@@ -185,11 +173,11 @@ export default function SelectValidationID({
// Look up the nodeId for this validation ID
let nodeId = validationIdToNodeId[formattedValue] || "";
-
+
// If not found directly, try the alternate format
if (!nodeId) {
- const alternateFormat = format === "hex"
- ? hexToCB58(formattedValue.slice(2))
+ const alternateFormat = format === "hex"
+ ? hexToCB58(formattedValue.slice(2))
: "0x" + cb58ToHex(formattedValue);
nodeId = validationIdToNodeId[alternateFormat] || "";
}
diff --git a/toolbox/src/stores/useAvaCloudSDK.ts b/toolbox/src/stores/useAvaCloudSDK.ts
new file mode 100644
index 00000000000..56ba0e8d19a
--- /dev/null
+++ b/toolbox/src/stores/useAvaCloudSDK.ts
@@ -0,0 +1,97 @@
+import { useMemo, useCallback } from "react";
+import { AvaCloudSDK } from "@avalabs/avacloud-sdk";
+import { GlobalParamNetwork } from "@avalabs/avacloud-sdk/models/components";
+import { useWalletStore } from "./walletStore";
+
+// Types for signature aggregation
+interface SignatureAggregationParams {
+ message: string;
+ signingSubnetId: string;
+ quorumPercentage?: number;
+}
+
+interface SignatureAggregationResult {
+ signedMessage: string;
+}
+
+// Types for L1 validators
+interface ListL1ValidatorsParams {
+ subnetId: string;
+ pageToken?: string;
+ pageSize?: number;
+}
+
+// Types for subnet operations
+interface GetSubnetByIdParams {
+ subnetId: string;
+}
+
+export const useAvaCloudSDK = (customNetwork?: GlobalParamNetwork) => {
+ const { isTestnet, getNetworkName } = useWalletStore();
+
+ // Determine network name
+ const networkName = useMemo(() => {
+ if (customNetwork) return customNetwork;
+ return getNetworkName();
+ }, [customNetwork, getNetworkName]);
+
+ // Create SDK instance
+ const sdk = useMemo(() => {
+ return new AvaCloudSDK({
+ serverURL: isTestnet ? "https://api.avax-test.network" : "https://api.avax.network",
+ network: networkName,
+ });
+ }, [isTestnet, networkName]);
+
+ // Signature aggregation method
+ const aggregateSignature = useCallback(async ({
+ message,
+ signingSubnetId,
+ quorumPercentage = 67,
+ }: SignatureAggregationParams): Promise => {
+ const result = await sdk.data.signatureAggregator.aggregate({
+ network: networkName,
+ signatureAggregatorRequest: {
+ message,
+ signingSubnetId,
+ quorumPercentage,
+ },
+ });
+ return result;
+ }, [sdk, networkName]);
+
+ // Primary Network - Subnet operations
+ const getSubnetById = useCallback(async ({ subnetId }: GetSubnetByIdParams) => {
+ return await sdk.data.primaryNetwork.getSubnetById({
+ network: networkName,
+ subnetId,
+ });
+ }, [sdk, networkName]);
+
+ // Primary Network - L1 Validator operations
+ const listL1Validators = useCallback(async ({
+ subnetId,
+ pageToken,
+ pageSize,
+ }: ListL1ValidatorsParams) => {
+ return await sdk.data.primaryNetwork.listL1Validators({
+ network: networkName,
+ subnetId,
+ pageToken,
+ pageSize,
+ });
+ }, [sdk, networkName]);
+
+ return {
+ // Raw SDK access for advanced usage
+ sdk,
+ networkName,
+
+ // Signature aggregation (most common pattern)
+ aggregateSignature,
+
+ // Primary Network API methods
+ getSubnetById,
+ listL1Validators,
+ };
+};
\ No newline at end of file
diff --git a/toolbox/src/stores/walletStore.ts b/toolbox/src/stores/walletStore.ts
index 9b0a7bb61ff..a35c9ef0d42 100644
--- a/toolbox/src/stores/walletStore.ts
+++ b/toolbox/src/stores/walletStore.ts
@@ -7,6 +7,7 @@ import { avalancheFuji, avalanche } from 'viem/chains';
import { zeroAddress } from 'viem';
import { getPChainBalance, getNativeTokenBalance, getChains } from '../coreViem/utils/glacier';
import debounce from 'debounce';
+import { GlobalParamNetwork } from "@avalabs/avacloud-sdk/models/components";
let indexedChainsPromise: Promise | null = null;
function getIndexedChains() {
@@ -107,7 +108,11 @@ export const useWalletStore = create(
debouncedUpdatePChainBalance();
debouncedUpdateL1Balance();
debouncedUpdateCChainBalance();
- }
+ },
+ getNetworkName: (): GlobalParamNetwork => {
+ const { avalancheNetworkID } = get();
+ return avalancheNetworkID === networkIDs.MainnetID ? "mainnet" : "fuji";
+ },
}
})
)
diff --git a/toolbox/src/toolbox/ICM/SendICMMessage.tsx b/toolbox/src/toolbox/ICM/SendICMMessage.tsx
index cd0223f3e42..1858d64e422 100644
--- a/toolbox/src/toolbox/ICM/SendICMMessage.tsx
+++ b/toolbox/src/toolbox/ICM/SendICMMessage.tsx
@@ -11,7 +11,7 @@ import ICMDemoABI from "../../../contracts/example-contracts/compiled/ICMDemo.js
import { utils } from "@avalabs/avalanchejs";
import { Input } from "../../components/Input";
import { Container } from "../../components/Container";
-import SelectChainID from "../../components/SelectChainID";
+import SelectBlockchainId from "../../components/SelectBlockchainId";
import { useL1ByChainId, useSelectedL1 } from "../../stores/l1ListStore";
import { useEffect } from "react";
const predeployedDemos: Record = {
@@ -174,7 +174,7 @@ export default function SendICMMessage() {
required
type="number"
/>
- setDestinationChainId(value)}
diff --git a/toolbox/src/toolbox/ICTT/AddCollateral.tsx b/toolbox/src/toolbox/ICTT/AddCollateral.tsx
index fe44a83dd9b..8e436ff886e 100644
--- a/toolbox/src/toolbox/ICTT/AddCollateral.tsx
+++ b/toolbox/src/toolbox/ICTT/AddCollateral.tsx
@@ -12,7 +12,7 @@ import { Input, Suggestion } from "../../components/Input";
import { EVMAddressInput } from "../../components/EVMAddressInput";
import { utils } from "@avalabs/avalanchejs";
import { Note } from "../../components/Note";
-import SelectChainID from "../../components/SelectChainID";
+import SelectBlockchainId from "../../components/SelectBlockchainId";
import { Container } from "../../components/Container";
import ERC20TokenRemoteABI from "../../../contracts/icm-contracts/compiled/ERC20TokenRemote.json";
import { getToolboxStore, useViemChainStore } from "../../stores/toolboxStore";
@@ -324,7 +324,7 @@ export default function AddCollateral() {
description="Approve and add collateral (ERC20 tokens) to the Token Home contract on the source chain for a remote bridge contract on the current chain."
>
- setSourceChainId(value)}
diff --git a/toolbox/src/toolbox/ICTT/DeployERC20TokenRemote.tsx b/toolbox/src/toolbox/ICTT/DeployERC20TokenRemote.tsx
index 178662ae00b..f98bd2c6535 100644
--- a/toolbox/src/toolbox/ICTT/DeployERC20TokenRemote.tsx
+++ b/toolbox/src/toolbox/ICTT/DeployERC20TokenRemote.tsx
@@ -15,7 +15,7 @@ import { Note } from "../../components/Note";
import { utils } from "@avalabs/avalanchejs";
import ERC20TokenHomeABI from "../../../contracts/icm-contracts/compiled/ERC20TokenHome.json";
import ExampleERC20 from "../../../contracts/icm-contracts/compiled/ExampleERC20.json";
-import SelectChainID from "../../components/SelectChainID";
+import SelectBlockchainId from "../../components/SelectBlockchainId";
import { Container } from "../../components/Container";
import TeleporterRegistryAddressInput from "../../components/TeleporterRegistryAddressInput";
@@ -220,7 +220,7 @@ export default function DeployERC20TokenRemote() {
}
- setSourceChainId(value)}
diff --git a/toolbox/src/toolbox/ICTT/DeployNativeTokenRemote.tsx b/toolbox/src/toolbox/ICTT/DeployNativeTokenRemote.tsx
index c83cf6c60a2..90af6dac77a 100644
--- a/toolbox/src/toolbox/ICTT/DeployNativeTokenRemote.tsx
+++ b/toolbox/src/toolbox/ICTT/DeployNativeTokenRemote.tsx
@@ -15,7 +15,7 @@ import { Note } from "../../components/Note";
import { utils } from "@avalabs/avalanchejs";
import ERC20TokenHomeABI from "../../../contracts/icm-contracts/compiled/ERC20TokenHome.json";
import ExampleERC20 from "../../../contracts/icm-contracts/compiled/ExampleERC20.json";
-import SelectChainID from "../../components/SelectChainID";
+import SelectBlockchainId from "../../components/SelectBlockchainId";
import { CheckPrecompile } from "../../components/CheckPrecompile";
import { Container } from "../../components/Container";
import TeleporterRegistryAddressInput from "../../components/TeleporterRegistryAddressInput";
@@ -207,7 +207,7 @@ export default function DeployNativeTokenRemote() {
}
- setSourceChainId(value)}
diff --git a/toolbox/src/toolbox/ICTT/RegisterWithHome.tsx b/toolbox/src/toolbox/ICTT/RegisterWithHome.tsx
index f0913e84dd1..ee798017edf 100644
--- a/toolbox/src/toolbox/ICTT/RegisterWithHome.tsx
+++ b/toolbox/src/toolbox/ICTT/RegisterWithHome.tsx
@@ -14,7 +14,7 @@ import { Suggestion } from "../../components/Input";
import { EVMAddressInput } from "../../components/EVMAddressInput";
import { utils } from "@avalabs/avalanchejs";
import { ListContractEvents } from "../../components/ListContractEvents";
-import SelectChainID from "../../components/SelectChainID";
+import SelectBlockchainId from "../../components/SelectBlockchainId";
import { Container } from "../../components/Container";
export default function RegisterWithHome() {
@@ -179,7 +179,7 @@ export default function RegisterWithHome() {
- setSourceChainId(value)}
diff --git a/toolbox/src/toolbox/ICTT/TestSend.tsx b/toolbox/src/toolbox/ICTT/TestSend.tsx
index 96cc36a7071..a07499a3783 100644
--- a/toolbox/src/toolbox/ICTT/TestSend.tsx
+++ b/toolbox/src/toolbox/ICTT/TestSend.tsx
@@ -18,7 +18,7 @@ import { Suggestion } from "../../components/TokenInput";
import { EVMAddressInput } from "../../components/EVMAddressInput";
import { Token, TokenInput } from "../../components/TokenInputToolbox";
import { utils } from "@avalabs/avalanchejs";
-import SelectChainID from "../../components/SelectChainID";
+import SelectBlockchain, { type BlockchainSelection } from "../../components/SelectBlockchain";
import { Container } from "../../components/Container";
import { Toggle } from "../../components/Toggle";
import { Ellipsis } from "lucide-react";
@@ -32,7 +32,7 @@ export default function TokenBridge() {
const selectedL1 = useSelectedL1()();
// Only need to select destination chain (source is current chain)
- const [destinationChainId, setDestinationChainId] = useState("");
+ const [destinationSelection, setDestinationSelection] = useState({ blockchainId: "", blockchain: null });
// Contract addresses
const [sourceContractAddress, setSourceContractAddress] = useState("");
@@ -72,17 +72,17 @@ export default function TokenBridge() {
const [tokenAllowance, setTokenAllowance] = useState(null);
// Get chain info - source is current chain, destination is selected
- const destL1 = useL1ByChainId(destinationChainId)();
- const destToolboxStore = getToolboxStore(destinationChainId)();
+ const destL1 = useL1ByChainId(destinationSelection.blockchainId)();
+ const destToolboxStore = getToolboxStore(destinationSelection.blockchainId)();
const { erc20TokenHomeAddress, nativeTokenHomeAddress } = useToolboxStore();
// Destination chain validation
let destChainError: string | undefined = undefined;
- if (!destinationChainId) {
- destChainError = "Please select a destination chain";
- } else if (destinationChainId === selectedL1?.id) {
- destChainError = "Source and destination chains must be different";
+ if (!destinationSelection.blockchainId) {
+ destChainError = "Please select a destination blockchain";
+ } else if (destinationSelection.blockchainId === selectedL1?.id) {
+ destChainError = "Source and destination blockchains must be different";
}
// Generate hex blockchain ID for the destination chain
@@ -91,7 +91,7 @@ export default function TokenBridge() {
try {
return utils.bufferToHex(utils.base58check.decode(destL1.id));
} catch (e) {
- console.error("Error decoding destination chain ID:", e);
+ console.error("Error decoding destination blockchain ID:", e);
return null;
}
}, [destL1?.id]);
@@ -478,10 +478,10 @@ export default function TokenBridge() {
description={`Send tokens from the current chain (${selectedL1?.name}) to another chain.`}
>
- setDestinationChainId(value)}
+
@@ -497,14 +497,14 @@ export default function TokenBridge() {
/>
setDestinationContractAddress(value as Address)}
verify={(value) => fetchTokenInfoFromBridgeContract(value as Address, "destination")}
- disabled={!destinationChainId || isProcessingSend || isProcessingApproval}
+ disabled={!destinationSelection.blockchainId || isProcessingSend || isProcessingApproval}
suggestions={destinationContractSuggestions}
- placeholder="0x... Bridge contract on destination chain"
+ placeholder="0x... Bridge contract on destination blockchain"
/>
(null);
const [chainID, setChainID] = useState("");
@@ -45,20 +46,17 @@ export default function CollectConversionSignatures() {
setIsConverting(true);
try {
- const { message, justification, signingSubnetId, networkId } = await coreWalletClient.extractWarpMessageFromPChainTx({ txId: conversionID });
+ const { message, justification, signingSubnetId } = await coreWalletClient.extractWarpMessageFromPChainTx({ txId: conversionID });
- const { signedMessage } = await new AvaCloudSDK().data.signatureAggregator.aggregateSignatures({
- network: networkId === networkIDs.FujiID ? "fuji" : "mainnet",
+ const networkName = getNetworkName();
+
+ const { signedMessage } = await sdk.data.signatureAggregator.aggregate({
+ network: networkName,
signatureAggregatorRequest: {
message: message,
justification: justification,
signingSubnetId: signingSubnetId,
- quorumPercentage: 67, // Default threshold for subnet validation
- },
- }, {
- retries: {
- strategy: "backoff",
- backoff: { initialInterval: 1000, maxInterval: 10000, exponent: 1.5, maxElapsedTime: 30 * 1000 },
+ quorumPercentage: 67,
}
});
@@ -81,10 +79,9 @@ export default function CollectConversionSignatures() {
onChange={setChainID}
error={chainIdError}
/>
-
({
+ subnetId: storeSubnetId,
+ subnet: null
+ });
const [chainID, setChainID] = useState(storeChainID);
const [isConverting, setIsConverting] = useState(false);
const [validators, setValidators] = useState([]);
@@ -59,7 +62,7 @@ export default function ConvertToL1() {
try {
const txID = await coreWalletClient.convertToL1({
managerAddress,
- subnetId: subnetId,
+ subnetId: selection.subnetId,
chainId: chainID,
subnetAuth: [0],
validators
@@ -79,9 +82,9 @@ export default function ConvertToL1() {
description="This will convert your Subnet to an L1."
>
-
@@ -120,10 +123,10 @@ export default function ConvertToL1() {
diff --git a/toolbox/src/toolbox/L1/CreateChain.tsx b/toolbox/src/toolbox/L1/CreateChain.tsx
index 446ed623161..0bd34778cf2 100644
--- a/toolbox/src/toolbox/L1/CreateChain.tsx
+++ b/toolbox/src/toolbox/L1/CreateChain.tsx
@@ -12,8 +12,8 @@ import { Step, Steps } from "fumadocs-ui/components/steps";
import generateName from 'boring-name-generator'
import { Success } from "../../components/Success";
import { RadioGroup } from "../../components/RadioGroup";
-
-export const EVM_VM_ID = "srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy"
+import InputSubnetId from "../../components/InputSubnetId";
+import { SUBNET_EVM_VM_ID } from "../Nodes/AvalanchegoDocker";
const generateRandomName = () => {
//makes sure the name doesn't contain a dash
@@ -47,8 +47,12 @@ export default function CreateChain() {
const [localChainName, setLocalChainName] = useState(generateRandomName());
const [showVMIdInput, setShowVMIdInput] = useState(false);
- const [vmId, setVmId] = useState(EVM_VM_ID);
+ const [vmId, setVmId] = useState(SUBNET_EVM_VM_ID);
+ // Wrapper function to handle subnet ID changes properly
+ const handleSubnetIdChange = (newSubnetId: string) => {
+ setSubnetID(newSubnetId);
+ };
async function handleCreateSubnet() {
setIsCreatingSubnet(true);
@@ -111,7 +115,7 @@ export default function CreateChain() {
disabled={true}
type="text"
/>
-
+
-
- {createdSubnetId && (
+
+ {createdSubnetId && (
+
- )}
-
+
+ )}
Step 2: Create a Chain
@@ -134,12 +139,12 @@ export default function CreateChain() {
Enter the parameters for your new chain.
-
setShowVMIdInput(value === "true")}
+ onChange={(value) => {
+ const shouldShow = value === "true";
+ setShowVMIdInput(shouldShow);
+ // Reset to standard EVM when switching to uncustomized
+ if (!shouldShow) {
+ setVmId(SUBNET_EVM_VM_ID);
+ }
+ }}
idPrefix={`show-vm-id`}
className="mb-4"
items={[
@@ -169,7 +181,7 @@ export default function CreateChain() {
value={vmId}
onChange={setVmId}
placeholder="Enter VM ID"
- helperText={`For an L1 with an uncustomized EVM use ${EVM_VM_ID}`}
+ helperText={`For an L1 with an uncustomized EVM use ${SUBNET_EVM_VM_ID}`}
/>
)}
diff --git a/toolbox/src/toolbox/L1/QueryL1Details.tsx b/toolbox/src/toolbox/L1/QueryL1Details.tsx
index 08251705948..0840f4ce509 100644
--- a/toolbox/src/toolbox/L1/QueryL1Details.tsx
+++ b/toolbox/src/toolbox/L1/QueryL1Details.tsx
@@ -1,75 +1,31 @@
-"use client"
+"use client";
-import { useWalletStore } from "../../stores/walletStore"
import { useState, useEffect } from "react"
import {
AlertCircle,
Info,
CheckCircle,
- Loader2,
Clock,
Users,
Database,
ExternalLink,
} from "lucide-react"
-import { networkIDs } from "@avalabs/avalanchejs"
-import { Button } from "../../components/Button"
import { Container } from "../../components/Container"
-import { AvaCloudSDK } from "@avalabs/avacloud-sdk"
-import { GlobalParamNetwork, Subnet } from "@avalabs/avacloud-sdk/models/components"
-import SelectSubnetId from "../../components/SelectSubnetId"
+import SelectSubnet, { SubnetSelection } from "../../components/SelectSubnet"
export default function QueryL1Details() {
- const [subnetId, setSubnetID] = useState("")
- const { avalancheNetworkID } = useWalletStore()
- const [subnetDetails, setSubnetDetails] = useState(null)
- const [isLoading, setIsLoading] = useState(false)
+ const [selection, setSelection] = useState({ subnetId: '', subnet: null })
const [error, setError] = useState(null)
- const [success, setSuccess] = useState(false)
- // Network names for display
- const networkNames: Record = {
- [networkIDs.MainnetID]: "mainnet",
- [networkIDs.FujiID]: "fuji",
- }
+ // Update error state when subnet details change
useEffect(() => {
- if (subnetId) {
- fetchSubnetDetails()
- }
- }, [subnetId, avalancheNetworkID])
-
- async function fetchSubnetDetails() {
- if (!subnetId) {
- setError("Please enter a subnet ID")
- return
+ if (selection.subnetId && !selection.subnet) {
+ setError("Failed to fetch subnet details")
+ } else {
+ setError(null)
}
-
- setIsLoading(true)
- setError(null)
- setSuccess(false)
-
- try {
- const network = networkNames[Number(avalancheNetworkID)]
- if (!network) {
- throw new Error("Invalid network selected")
- }
-
- const subnet = await new AvaCloudSDK().data.primaryNetwork.getSubnetById({
- network: network,
- subnetId,
- });
-
- setSubnetDetails(subnet)
- setSuccess(true)
- } catch (error: any) {
- setError(error.message || "Failed to fetch subnet details")
- setSubnetDetails(null)
- console.error("Error fetching subnet details:", error)
- } finally {
- setIsLoading(false)
- }
- }
+ }, [selection])
function formatTimestamp(timestamp: number): string {
return new Date(timestamp * 1000).toLocaleString()
@@ -90,43 +46,16 @@ export default function QueryL1Details() {
)}
- {success && !subnetDetails && (
-
-
-
- Subnet details retrieved successfully
-
-
- )}
-
-
-
-
- {subnetDetails && (
+ {selection.subnet && (
{/* Background gradient effect - blue for both light and dark mode */}
@@ -144,7 +73,7 @@ export default function QueryL1Details() {
Subnet ID:
- {subnetDetails.subnetId}
+ {selection.subnet.subnetId}
@@ -152,16 +81,16 @@ export default function QueryL1Details() {
- {subnetDetails.isL1 ? "Sovereign L1" : "Subnet"}
+ {selection.subnet.isL1 ? "Sovereign L1" : "Subnet"}
@@ -182,13 +111,13 @@ export default function QueryL1Details() {
Created:
- {formatTimestamp(subnetDetails.createBlockTimestamp)}
+ {formatTimestamp(selection.subnet.createBlockTimestamp)}
Block Index:
- {subnetDetails.createBlockIndex}
+ {selection.subnet.createBlockIndex}
@@ -196,7 +125,7 @@ export default function QueryL1Details() {
{/* L1 Specific Information */}
- {subnetDetails.isL1 && (
+ {selection.subnet.isL1 && (
@@ -208,22 +137,22 @@ export default function QueryL1Details() {
- {subnetDetails.l1ValidatorManagerDetails && (
+ {selection.subnet.l1ValidatorManagerDetails && (