diff --git a/components/tools/common/api/validator-info.ts b/components/tools/common/api/validator-info.ts
index d5ece29657c..3d8dd606fb4 100644
--- a/components/tools/common/api/validator-info.ts
+++ b/components/tools/common/api/validator-info.ts
@@ -2,6 +2,7 @@ import { Validator, SubnetInfo, L1ValidatorManagerDetails } from './types';
import { pChainEndpoint } from './consts';
import { AvaCloudSDK } from "@avalabs/avacloud-sdk";
export const avaCloudSDK = new AvaCloudSDK({
+ serverURL: "https://api.avax.network",
chainId: "43114",
network: "fuji",
});
diff --git a/package.json b/package.json
index 91a1b96a275..4a6f6baa817 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",
@@ -128,4 +128,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 fce5d3ec7b0..ead300e0986 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",
@@ -72,4 +72,4 @@
"vite": "^6.1.6",
"vite-plugin-dts": "^4.5.0"
}
-}
+}
\ 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..124f195fb82
--- /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 { useWalletStore } from "../stores/walletStore";
+import { useMemo, useState, useCallback, useEffect } from "react";
+import { AvaCloudSDK } from "@avalabs/avacloud-sdk";
+import { networkIDs } from "@avalabs/avalanchejs";
+import { GlobalParamNetwork } from "@avalabs/avacloud-sdk/models/components";
+
+// Primary network subnet ID
+const PRIMARY_NETWORK_SUBNET_ID = "11111111111111111111111111111111LpoYY";
+
+export default function InputSubnetId({
+ value,
+ onChange,
+ error,
+ label = "Subnet ID",
+ hidePrimaryNetwork = false,
+ helperText,
+ id
+}: {
+ value: string,
+ onChange: (value: string) => void,
+ error?: string | null,
+ label?: string,
+ hidePrimaryNetwork?: boolean
+ helperText?: string | null
+ id?: string
+}) {
+ const { avalancheNetworkID } = useWalletStore();
+ const createChainStoreSubnetId = useCreateChainStore()(state => state.subnetId);
+ const { l1List } = useL1ListStore()();
+
+ const [validationError, setValidationError] = useState(null);
+
+ // Network names for API calls
+ const networkNames: Record = {
+ [networkIDs.MainnetID]: "mainnet",
+ [networkIDs.FujiID]: "fuji",
+ };
+
+ // Validate subnet ID using AvaCloud SDK
+ const validateSubnetId = useCallback(async (subnetId: string) => {
+ if (!subnetId || subnetId.length < 10) {
+ setValidationError(null);
+ return;
+ }
+
+ try {
+ setValidationError(null);
+
+ const network = networkNames[Number(avalancheNetworkID)];
+ if (!network) {
+ setValidationError(null);
+ return;
+ }
+
+ const sdk = new AvaCloudSDK({
+ serverURL: "https://api.avax.network",
+ network: network,
+ });
+
+ await sdk.data.primaryNetwork.getSubnetById({
+ network: network,
+ 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");
+ }
+ }, [avalancheNetworkID, networkNames]);
+
+ // Validate when value changes
+ useEffect(() => {
+ const timeoutId = setTimeout(() => {
+ if (value) {
+ validateSubnetId(value);
+ } else {
+ setValidationError(null);
+ }
+ }, 500); // Debounce validation
+
+ return () => clearTimeout(timeoutId);
+ }, [value, validateSubnetId]);
+
+ 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/SelectSubnet.tsx b/toolbox/src/components/SelectSubnet.tsx
new file mode 100644
index 00000000000..f3401e03384
--- /dev/null
+++ b/toolbox/src/components/SelectSubnet.tsx
@@ -0,0 +1,102 @@
+"use client"
+
+import SelectSubnetId from "./SelectSubnetId";
+import { useState, useCallback } from "react";
+import { AvaCloudSDK } from "@avalabs/avacloud-sdk";
+import { useWalletStore } from "../stores/walletStore";
+import { networkIDs } from "@avalabs/avalanchejs";
+import { GlobalParamNetwork, Subnet } from "@avalabs/avacloud-sdk/models/components";
+import SubnetDetailsDisplay from "./SubnetDetailsDisplay";
+
+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 { avalancheNetworkID } = useWalletStore();
+ const [subnetDetails, setSubnetDetails] = useState>({});
+ const [isLoading, setIsLoading] = useState(false);
+
+ // Network names for API calls
+ const networkNames: Record = {
+ [networkIDs.MainnetID]: "mainnet",
+ [networkIDs.FujiID]: "fuji",
+ };
+
+ // Fetch subnet details when needed
+ const fetchSubnetDetails = useCallback(async (subnetId: string) => {
+ if (!subnetId || subnetDetails[subnetId]) return;
+
+ try {
+ const network = networkNames[Number(avalancheNetworkID)];
+ if (!network) return;
+
+ setIsLoading(true);
+ const sdk = new AvaCloudSDK({
+ serverURL: "https://api.avax.network",
+ network: network,
+ });
+
+ const subnet = await sdk.data.primaryNetwork.getSubnetById({
+ network: network,
+ subnetId,
+ });
+
+ setSubnetDetails(prev => ({
+ ...prev,
+ [subnetId]: subnet
+ }));
+ } catch (error) {
+ console.error(`Error fetching subnet details for ${subnetId}:`, error);
+ } finally {
+ setIsLoading(false);
+ }
+ }, [avalancheNetworkID, networkNames, subnetDetails]);
+
+ // 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]);
+
+ // 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 a45df13ff7e..b0349c4e747 100644
--- a/toolbox/src/components/SelectValidationID.tsx
+++ b/toolbox/src/components/SelectValidationID.tsx
@@ -43,16 +43,16 @@ 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"
}) {
@@ -71,13 +71,18 @@ export default function SelectValidationID({
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({
+ const sdk = new AvaCloudSDK({
+ serverURL: "https://api.avax.network",
+ network: network,
+ });
+
+ const result = await sdk.data.primaryNetwork.listL1Validators({
network: network,
subnetId: subnetId,
});
@@ -87,7 +92,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
@@ -117,10 +122,10 @@ export default function SelectValidationID({
// 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(() => {
@@ -133,7 +138,7 @@ export default function SelectValidationID({
const nodeId = validator.nodeId;
const weightDisplay = validator.weight.toLocaleString();
const isSelected = nodeId === selectedNodeId;
-
+
// Add just one version based on the format prop
if (format === "hex") {
try {
@@ -180,11 +185,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/components/SubnetDetailsDisplay.tsx b/toolbox/src/components/SubnetDetailsDisplay.tsx
new file mode 100644
index 00000000000..2a644472e11
--- /dev/null
+++ b/toolbox/src/components/SubnetDetailsDisplay.tsx
@@ -0,0 +1,287 @@
+"use client"
+
+import type { Subnet } from "@avalabs/avacloud-sdk/models/components"
+import { Calendar, Users, Database, Key, Copy, AlertTriangle, FileText } from "lucide-react"
+import { useState } from "react"
+
+interface SubnetDetailsDisplayProps {
+ subnet: Subnet | null
+ isLoading?: boolean
+}
+
+export default function SubnetDetailsDisplay({ subnet, isLoading }: SubnetDetailsDisplayProps) {
+ const [copiedText, setCopiedText] = useState(null)
+
+ // Standard EVM VM ID
+ const STANDARD_EVM_VM_ID = "srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy"
+
+ if (isLoading) {
+ return (
+
+
+
+
+
Loading subnet details...
+
+
+
+ )
+ }
+
+ if (!subnet) {
+ return null
+ }
+
+ const formatTimestamp = (timestamp: number) => {
+ const date = new Date(timestamp * 1000)
+ const now = new Date()
+
+ if (date > now && date.getFullYear() > now.getFullYear() + 1) {
+ return new Date(timestamp).toLocaleDateString("en-US", {
+ year: "2-digit",
+ month: "short",
+ day: "numeric",
+ })
+ }
+
+ return date.toLocaleDateString("en-US", {
+ year: "2-digit",
+ month: "short",
+ day: "numeric",
+ })
+ }
+
+ const copyToClipboard = (text: string | undefined | null) => {
+ if (!text) return
+ navigator.clipboard.writeText(text)
+ setCopiedText(text)
+ setTimeout(() => setCopiedText(null), 2000)
+ }
+
+ return (
+
+ {/* Lighter Gray Header */}
+
+
+
Subnet Details
+
+ {subnet.isL1 ? "L1 Chain" : "Subnet"}
+
+
+
+
+ {/* White Content Area */}
+
+ {/* Basic Information */}
+
+ {/* Left Column - Created & Chains */}
+
+
+
+ Created:
+ {formatTimestamp(subnet.createBlockTimestamp)}
+
+
+
+
+ Chains:
+ {subnet.blockchains?.length || 0}
+
+
+
+ {/* Right Column - Owner Address & Threshold */}
+
+ {subnet.subnetOwnershipInfo.addresses && subnet.subnetOwnershipInfo.addresses.length > 0 && (
+
+
+
+
Owner Address:
+
+
+ {subnet.subnetOwnershipInfo.addresses[0]}
+
+ copyToClipboard(subnet.subnetOwnershipInfo.addresses[0])}
+ >
+
+
+
+
+
+
+ {subnet.subnetOwnershipInfo.threshold} of {subnet.subnetOwnershipInfo.addresses.length} signatures required
+
+
+ )}
+
+
+
+ {/* Additional Owner Addresses if more than one */}
+ {subnet.subnetOwnershipInfo.addresses && subnet.subnetOwnershipInfo.addresses.length > 1 && (
+
+
Additional Owners:
+
+ {subnet.subnetOwnershipInfo.addresses.slice(1, 3).map((address, index) => (
+
+
+ {address}
+
+ copyToClipboard(address)}
+ >
+
+
+
+ ))}
+ {subnet.subnetOwnershipInfo.addresses.length > 3 && (
+
+ +{subnet.subnetOwnershipInfo.addresses.length - 3} more
+
+ )}
+
+
+ )}
+
+ {/* Blockchain Information */}
+ {subnet.blockchains && subnet.blockchains.length > 0 && (
+
+
+
+ Blockchain Details:
+
+
+
+ {subnet.blockchains.map((blockchain, index) => {
+ const blockchainAny = blockchain as any
+ const isNonStandardVM = blockchainAny.vmId && blockchainAny.vmId !== STANDARD_EVM_VM_ID
+
+ return (
+
+
+ {blockchainAny.blockchainName || "Blockchain"}
+
+
+
+ {blockchainAny.evmChainId && (
+
+
EVM Chain ID:
+
+
+ {blockchainAny.evmChainId}
+
+ copyToClipboard(blockchainAny.evmChainId.toString())}
+ >
+
+
+
+
+ )}
+
+
+
Blockchain ID:
+
+
+ {blockchainAny.blockchainId}
+
+ copyToClipboard(blockchainAny.blockchainId)}
+ >
+
+
+
+
+
+
+
VM ID:
+
+
+ {blockchainAny.vmId}
+
+ copyToClipboard(blockchainAny.vmId)}
+ >
+
+
+
+
+
+
+ {/* Warning for non-standard VM */}
+ {isNonStandardVM && (
+
+
+
Non-standard VM detected
+
+ )}
+
+ )
+ })}
+
+
+ )}
+
+ {/* L1 Conversion Information */}
+ {subnet.isL1 && subnet.l1ValidatorManagerDetails && (
+
+
+
+ L1 Conversion:
+
+
+
+ {subnet.l1ConversionTransactionHash && (
+
+
Conversion Tx:
+
+
+ {subnet.l1ConversionTransactionHash}
+
+ copyToClipboard(subnet.l1ConversionTransactionHash)}
+ >
+
+
+
+
+ )}
+
+
+
Validator Manager:
+
+
+ {(subnet.l1ValidatorManagerDetails as any).contractAddress}
+
+ copyToClipboard((subnet.l1ValidatorManagerDetails as any).contractAddress)}
+ >
+
+
+
+
+
+
+ )}
+
+ {/* Copy feedback */}
+ {copiedText && (
+
+ Copied to clipboard!
+
+ )}
+
+
+ )
+}
diff --git a/toolbox/src/toolbox/L1/CollectConversionSignatures.tsx b/toolbox/src/toolbox/L1/CollectConversionSignatures.tsx
index b4a98281d9c..9dccd096937 100644
--- a/toolbox/src/toolbox/L1/CollectConversionSignatures.tsx
+++ b/toolbox/src/toolbox/L1/CollectConversionSignatures.tsx
@@ -9,8 +9,16 @@ import { CodeHighlighter } from "../../components/CodeHighlighter";
import { Container } from "../../components/Container";
import { Input } from "../../components/Input";
import InputChainId from "../../components/InputChainId";
+import InputSubnetId from "../../components/InputSubnetId";
import { getBlockchainInfo, getSubnetInfo } from "../../coreViem/utils/glacier";
import { ResultField } from "../../components/ResultField";
+import { GlobalParamNetwork } from "@avalabs/avacloud-sdk/models/components";
+
+// Network names for API calls
+const networkNames: Record = {
+ [networkIDs.MainnetID]: "mainnet",
+ [networkIDs.FujiID]: "fuji",
+};
export default function CollectConversionSignatures() {
const { coreWalletClient } = useWalletStore();
@@ -47,18 +55,16 @@ export default function CollectConversionSignatures() {
try {
const { message, justification, signingSubnetId, networkId } = await coreWalletClient.extractWarpMessageFromPChainTx({ txId: conversionID });
- const { signedMessage } = await new AvaCloudSDK().data.signatureAggregator.aggregateSignatures({
- network: networkId === networkIDs.FujiID ? "fuji" : "mainnet",
+ const { signedMessage } = await new AvaCloudSDK({
+ serverURL: "https://api.avax.network",
+ network: networkNames[Number(networkId)],
+ }).data.signatureAggregator.aggregate({
+ network: networkNames[Number(networkId)],
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 +87,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 chain."
>
-
@@ -120,10 +123,10 @@ export default function ConvertToL1() {
- Convert to L1
+ {selection.subnet?.isL1 ? "Subnet Already Converted to L1" : "Convert to L1"}
diff --git a/toolbox/src/toolbox/L1/CreateChain.tsx b/toolbox/src/toolbox/L1/CreateChain.tsx
index cadc1876ec2..74f41852e22 100644
--- a/toolbox/src/toolbox/L1/CreateChain.tsx
+++ b/toolbox/src/toolbox/L1/CreateChain.tsx
@@ -12,6 +12,7 @@ import { Step, Steps } from "fumadocs-ui/components/steps";
import generateName from 'boring-name-generator'
import { Success } from "../../components/Success";
import { RadioGroup } from "../../components/RadioGroup";
+import InputSubnetId from "../../components/InputSubnetId";
export const EVM_VM_ID = "srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy"
@@ -40,16 +41,20 @@ export default function CreateChain() {
const [isCreatingSubnet, setIsCreatingSubnet] = useState(false);
const [createdSubnetId, setCreatedSubnetId] = useState("");
-
+
const [isCreatingChain, setIsCreatingChain] = useState(false);
const [createdChainId, setCreatedChainId] = useState("");
-
+
const [localGenesisData, setLocalGenesisData] = useState(genesisData);
const [localChainName, setLocalChainName] = useState(generateRandomName());
const [showVMIdInput, setShowVMIdInput] = useState(false);
const [vmId, setVmId] = useState(EVM_VM_ID);
+ // Wrapper function to handle subnet ID changes properly
+ const handleSubnetIdChange = (newSubnetId: string) => {
+ setSubnetID(newSubnetId);
+ };
async function handleCreateSubnet() {
setIsCreatingSubnet(true);
@@ -121,10 +126,12 @@ export default function CreateChain() {
{createdSubnetId && (
-
+
+
+
)}
@@ -133,12 +140,11 @@ export default function CreateChain() {
Enter the parameters for your new chain.
-
-
+ >
Create Chain
- {createdChainId && }
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
-
-
- )}
-
-
-
-
- {isLoading ? (
- <>
-
- Fetching Details...
- >
- ) : (
- "Fetch Subnet Details"
- )}
-
- {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 && (