Skip to content

VMC on C-Chain/any L1 #2376

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
May 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions toolbox/src/coreViem/methods/extractWarpMessageFromPChainTx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,48 @@ export type ExtractWarpMessageFromTxParams = {
export type ExtractWarpMessageFromTxResponse = {
message: string;
justification: string;
subnetId: string;
signingSubnetId: string;
networkId: typeof networkIDs.FujiID | typeof networkIDs.MainnetID;
validators: Validator[];
chainId: string;
managerAddress: string;
}

// FIXME: This should be included in avacloud-sdk but I'm afraid to version bump right now
// if you have better idea to get the subnetId from a blockchainId, please go ahead and change it
/**
* Fetches blockchain information from Glacier API
* @param network "fuji" or "mainnet"
* @param blockchainId The blockchain ID to query
* @returns The subnet ID associated with the blockchain
*/
export async function getSubnetIdFromChainId(network: "fuji" | "mainnet", blockchainId: string): Promise<string> {
try {
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(`Glacier API returned ${response.status}: ${response.statusText}`);
}

const data = await response.json();

if (!data.subnetId) {
throw new Error('No subnetId found in response');
}

return data.subnetId;
} catch (error) {
console.error('Error fetching subnet info from Glacier:', error);
throw error;
}
}

//TODO: rename
export async function extractWarpMessageFromPChainTx(client: WalletClient<any, any, any, CoreWalletRpcSchema>, { txId }: ExtractWarpMessageFromTxParams): Promise<ExtractWarpMessageFromTxResponse> {
const isTestnetMode = await isTestnet(client);
Expand Down Expand Up @@ -146,11 +181,13 @@ export async function extractWarpMessageFromPChainTx(client: WalletClient<any, a
};

const [message, justification] = packL1ConversionMessage(conversionArgs, networkId, data.result.tx.unsignedTx.blockchainID);

const network = networkId === networkIDs.FujiID ? "fuji" : "mainnet";
const signingSubnetId = await getSubnetIdFromChainId(network, data.result.tx.unsignedTx.chainID);
return {
message: utils.bufferToHex(message),
justification: utils.bufferToHex(justification),
signingSubnetId: data.result.tx.unsignedTx.subnetID,
subnetId: data.result.tx.unsignedTx.subnetID,
signingSubnetId: signingSubnetId,
networkId,
validators: data.result.tx.unsignedTx.validators,
chainId: data.result.tx.unsignedTx.chainID,
Expand Down
4 changes: 2 additions & 2 deletions toolbox/src/toolbox/L1/ConvertToL1.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ import { TextareaArray } from "../components/TextareaArray";
export default function ConvertToL1() {
const {
subnetId,
chainID,
chainID: storeChainID,
setSubnetID,
setChainID,
nodePopJsons,
setNodePopJsons,
managerAddress,
Expand All @@ -29,6 +28,7 @@ export default function ConvertToL1() {
setValidatorWeights,
} = useCreateChainStore()();

const [chainID, setChainID] = useState(storeChainID);
const [isConverting, setIsConverting] = useState(false);
const [validatorBalances, setValidatorBalances] = useState(Array(100).fill(BigInt(1000000000)) as bigint[]);
const { coreWalletClient, pChainAddress } = useWalletStore();
Expand Down
78 changes: 28 additions & 50 deletions toolbox/src/toolbox/ValidatorManager/ChangeWeight.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
import { useState } from "react"
import { useErrorBoundary } from "react-error-boundary"

import { useSelectedL1, useViemChainStore } from "../toolboxStore"
import { useSelectedL1, useViemChainStore, useCreateChainStore } from "../toolboxStore"
import { useWalletStore } from "../../lib/walletStore"

import { Container } from "../components/Container"
import { Input } from "../../components/Input"
import { Button } from "../../components/Button"
import { StepIndicator } from "../components/StepIndicator"
import { AlertCircle, CheckCircle } from "lucide-react"
import SelectSubnetId from "../components/SelectSubnetId"

import { cn } from "../../lib/utils"
import { bytesToHex, hexToBytes } from "viem"
Expand Down Expand Up @@ -48,13 +49,14 @@ export default function ChangeWeight() {

const { coreWalletClient, pChainAddress, avalancheNetworkID, publicClient } = useWalletStore()
const viemChain = useViemChainStore()
const selectedL1 = useSelectedL1()();
const selectedL1 = useSelectedL1()()
const createChainStoreSubnetId = useCreateChainStore()(state => state.subnetId)

// --- Form Input State ---
const [nodeID, setNodeID] = useState("")
const [weight, setWeight] = useState("")
const [manualProxyAddress, setManualProxyAddress] = useState(selectedL1?.validatorManagerAddress || "")
const [manualSubnetId, setManualSubnetId] = useState(selectedL1?.subnetId || "")
const [currentSubnetId, setCurrentSubnetId] = useState(createChainStoreSubnetId || selectedL1?.subnetId || "")

// --- Intermediate Data State ---
const [validationIDHex, setValidationIDHex] = useState("")
Expand Down Expand Up @@ -226,11 +228,15 @@ export default function ChangeWeight() {
throw new Error("Warp message is empty. Retry step 2.")
}

if (!selectedL1?.subnetId) {
throw new Error("No subnet ID available. Please select a valid L1 or specify a subnet ID.")
}

const { signedMessage: signedMessageResult } = await new AvaCloudSDK().data.signatureAggregator.aggregateSignatures({
network: networkName,
signatureAggregatorRequest: {
message: warpMessageToSign, // Use potentially updated local message
signingSubnetId: manualSubnetId || "",
message: warpMessageToSign,
signingSubnetId: selectedL1.subnetId,
quorumPercentage: 67,
},
})
Expand Down Expand Up @@ -285,14 +291,14 @@ export default function ChangeWeight() {
const eventDataForPacking = localEventData || eventData;

if (!viemChain) throw new Error("Viem chain configuration is missing.")
if (!validationIDForJustification) throw new Error("Validation ID is missing. Retry step 1.")
if (!manualSubnetId) throw new Error("Subnet ID is missing.")
if (!validationIDForJustification) throw new Error("Validation ID is missing.")
if (!currentSubnetId) throw new Error("Subnet ID is missing.")
if (!eventDataForPacking) throw new Error("Event data missing. Retry step 2.")

const justification = await GetRegistrationJustification(
nodeID,
validationIDForJustification,
manualSubnetId,
currentSubnetId,
publicClient
)

Expand All @@ -316,12 +322,16 @@ export default function ChangeWeight() {
console.log("Change Weight Message Hex:", bytesToHex(changeWeightMessage))
console.log("Justification:", justification)

if (!selectedL1?.subnetId) {
throw new Error("No subnet ID available. Please select a valid L1 or specify a subnet ID.")
}

const signature = await new AvaCloudSDK().data.signatureAggregator.aggregateSignatures({
network: networkName,
signatureAggregatorRequest: {
message: bytesToHex(changeWeightMessage),
justification: bytesToHex(justification),
signingSubnetId: manualSubnetId || "",
signingSubnetId: selectedL1.subnetId,
quorumPercentage: 67,
},
})
Expand Down Expand Up @@ -357,9 +367,8 @@ export default function ChangeWeight() {
if (!publicClient) throw new Error("Public client is not initialized.")
if (!viemChain) throw new Error("Viem chain is not configured.")

let simulationResult
try {
simulationResult = await publicClient.simulateContract({

const hash = await coreWalletClient.writeContract({
address: manualProxyAddress as `0x${string}`,
abi: validatorManagerAbi.abi,
functionName: "completeValidatorWeightUpdate",
Expand All @@ -368,30 +377,11 @@ export default function ChangeWeight() {
account: coreWalletClient.account,
chain: viemChain
})
console.log("Simulation successful:", simulationResult)
} catch (simError: any) {
console.error("Contract simulation failed:", simError)
const baseError = simError.cause || simError
const reason = baseError?.shortMessage || simError.message || "Simulation failed, reason unknown."
throw new Error(`Contract simulation failed: ${reason}`)
}

console.log("Simulation request:", simulationResult.request)

let txHash
try {
txHash = await coreWalletClient.writeContract(simulationResult.request)
console.log("Transaction sent:", txHash)
} catch (writeError: any) {
console.error("Contract write failed:", writeError)
const baseError = writeError.cause || writeError
const reason = baseError?.shortMessage || writeError.message || "Transaction submission failed, reason unknown."
throw new Error(`Submitting transaction failed: ${reason}`)
}
console.log("Transaction sent:", hash)

let receipt
try {
receipt = await publicClient.waitForTransactionReceipt({ hash: txHash })
receipt = await publicClient.waitForTransactionReceipt({ hash })
console.log("Transaction receipt:", receipt)
if (receipt.status !== 'success') {
throw new Error(`Transaction failed with status: ${receipt.status}`)
Expand Down Expand Up @@ -507,25 +497,13 @@ export default function ChangeWeight() {
</div>

<div className="space-y-2">
<Input
id="subnetId"
type="text"
value={manualSubnetId}
onChange={setManualSubnetId}
placeholder={"Enter subnet ID"}
className={cn(
"w-full px-3 py-2 border rounded-md",
"text-zinc-900 dark:text-zinc-100",
"bg-white dark:bg-zinc-800",
"border-zinc-300 dark:border-zinc-700",
"focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary",
"placeholder:text-zinc-400 dark:placeholder:text-zinc-500",
)}
label="Subnet ID"
disabled={isProcessing}
<SelectSubnetId
value={currentSubnetId}
onChange={setCurrentSubnetId}
error={null}
/>
<p className="text-xs text-zinc-500 dark:text-zinc-400">
Override the current subnet ID (or leave empty to use default)
Select a subnet ID (defaults to subnet from Create Subnet tool if available)
</p>
</div>

Expand Down
18 changes: 9 additions & 9 deletions toolbox/src/toolbox/ValidatorManager/InitValidatorSet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default function InitValidatorSet() {
const { coreWalletClient, publicClient } = useWalletStore();
const [isInitializing, setIsInitializing] = useState(false);
const [txHash, setTxHash] = useState<string | null>(null);
const [simulationWentThrough, setSimulationWentThrough] = useState(false);
const [simulationWentThrough, _] = useState(false);
const [error, setError] = useState<string | null>(null);
const [collectedData, setCollectedData] = useState<Record<string, any>>({});
const [showDebugData, setShowDebugData] = useState(false);
Expand Down Expand Up @@ -90,12 +90,11 @@ export default function InitValidatorSet() {
try {
if (!coreWalletClient) throw new Error('Core wallet client not found');

const { validators, signingSubnetId, chainId, managerAddress } = await coreWalletClient.extractWarpMessageFromPChainTx({ txId: conversionTxID });

const { validators, subnetId, chainId, managerAddress } = await coreWalletClient.extractWarpMessageFromPChainTx({ txId: conversionTxID });
// Prepare transaction arguments
const txArgs = [
{
subnetID: cb58ToHex(signingSubnetId),
subnetID: cb58ToHex(subnetId),
validatorManagerBlockchainID: cb58ToHex(chainId),
validatorManagerAddress: managerAddress as `0x${string}`,
initialValidators: validators
Expand Down Expand Up @@ -128,7 +127,8 @@ export default function InitValidatorSet() {
const signatureBytes = hexToBytes(add0x(L1ConversionSignature));
const accessList = packWarpIntoAccessList(signatureBytes);

const sim = await publicClient.simulateContract({
// FIXME: for whatever reason, viem simulation does not work consistently, so we just send the transaction
const hash = await coreWalletClient.writeContract({
address: managerAddress as `0x${string}`,
abi: ValidatorManagerABI.abi,
functionName: 'initializeValidatorSet',
Expand All @@ -138,14 +138,14 @@ export default function InitValidatorSet() {
chain: viemChain || undefined,
});

console.log("Simulated transaction:", sim);
setSimulationWentThrough(true);
// console.log("Simulated transaction:", sim);
// setSimulationWentThrough(true);

console.log("sim", JSON.stringify(sim, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2));
// console.log("sim", JSON.stringify(sim, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2));


// Send transaction
const hash = await coreWalletClient.writeContract(sim.request);
// const hash = await coreWalletClient.writeContract(sim.request);

// Wait for transaction confirmation
const receipt = await publicClient.waitForTransactionReceipt({ hash });
Expand Down
Loading