Skip to content
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

PoA Validator Manager Upgrade #2176

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
import NextPrev from "@/components/tools/common/ui/NextPrev";
import { usePoAValidatorManagementWizardStore } from "../config/store";
import { useState } from "react";
import { checkCoreWallet } from '@/components/tools/common/api/coreWallet';

export default function Welcome() {
const { goToNextStep, goToPreviousStep } = usePoAValidatorManagementWizardStore();
const [error, setError] = useState<string | null>(null);

const handleNext = async () => {
try {
await checkCoreWallet();
goToNextStep();
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to check Core wallet");
}
};

const [error, _] = useState<string | null>(null);
return <>
<h1 className="text-2xl font-medium mb-6">Welcome</h1>

Expand Down Expand Up @@ -63,7 +52,7 @@ export default function Welcome() {
<NextPrev
nextDisabled={false}
prevHidden={false}
onNext={handleNext}
onNext={goToNextStep}
onPrev={goToPreviousStep}
/>
</>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ import { Label } from '@radix-ui/react-label';
import { Input } from '@/components/ui/input';
import { CheckCircle2, XCircle } from 'lucide-react';
import NextPrev from "@/components/tools/common/ui/NextPrev";
import RequireWalletConnection from '@/components/tools/common/ui/RequireWalletConnection__deprecated';

import RequireWalletConnection from '@/components/tools/common/ui/RequireWalletConnectionV2';
import { usePoAValidatorManagementWizardStore } from '../config/store';

import PoAValidatorManagerAbi from '../contract_compiler/compiled/PoAValidatorManager.json';
import { Address, Chain, createPublicClient, createWalletClient, custom, defineChain, http } from 'viem';
import { Address, createPublicClient, http, createWalletClient, custom, PublicClient } from 'viem';

import {
fetchValidators,
Expand All @@ -21,7 +19,6 @@ import {

import { isValidUrl } from '@/components/tools/common/utils/validation';
import { isAddress } from 'viem';
import { deduplicateEthRequestAccounts } from '../../L1Launcher/config/store';

export default function ChainParameters() {
const {
Expand All @@ -41,8 +38,10 @@ export default function ChainParameters() {
goToPreviousStep,
setValidators,
setSubnetId,
setChainConfig,
setCoreWalletClient
getViemL1Chain,
setCoreWalletClient,
setChainId,
setRpcAddress
} = usePoAValidatorManagementWizardStore();

const [error, setError] = useState("");
Expand All @@ -61,8 +60,32 @@ export default function ChainParameters() {
setIsLoading(true);
setError("");
setShowWallet(false);
setOwnerCheckStatus('none');

try {
// Parse blockchainID from RPC URL
const blockchainIdMatch = rpcUrl.match(/\/ext\/bc\/([^\/]+)\/rpc/);
if (blockchainIdMatch && blockchainIdMatch[1]) {
const blockchainId = blockchainIdMatch[1];
setChainId(blockchainId);
console.log('Parsed blockchainID:', blockchainId);
} else {
console.warn('Could not parse blockchainID from RPC URL');
}

// Extract and set the base URL
try {
const url = new URL(rpcUrl);
const baseUrlPath = url.pathname.split('/ext/bc/')[0];
const baseUrl = url.origin + baseUrlPath;
// Remove the protocol prefix (http:// or https://) before setting
const baseUrlWithoutProtocol = baseUrl.replace(/^https?:\/\//, '');
setRpcAddress(baseUrlWithoutProtocol);
console.log('Set base URL (without protocol):', baseUrlWithoutProtocol);
} catch (urlError) {
console.warn('Could not parse base URL from RPC URL:', urlError);
}

// First validate the endpoints
const endpoints = getEndpoints(rpcUrl);
console.log('Generated endpoints:', endpoints);
Expand Down Expand Up @@ -115,12 +138,13 @@ export default function ChainParameters() {
throw new Error(data.error.message);
}

let publicClient: PublicClient | undefined;
if (data.result && data.result.chainId) {
setEvmChainId(Number(data.result.chainId));

// Try to get chain name using eth_chainId
try {
const publicClient = createPublicClient({
publicClient = createPublicClient({
transport: http(rpcUrl)
});

Expand All @@ -147,13 +171,6 @@ export default function ChainParameters() {
if (validators.length > 0 && validators[0].validationID) {
subnetId = await fetchSubnetIdByValidationID(validators[0].validationID);
setSubnetId(subnetId);
// const validatorResponse = await avaCloudSDK.data.primaryNetwork.listL1Validators({
// l1ValidationId: validators[0].validationID,
// network: "fuji",
// }) as ListL1ValidatorsResponse;
// console.log('validatorResponse', validatorResponse);
// subnetId = validatorResponse.result.validators[0].subnetId;

}
console.log('subnetId', subnetId);
try {
Expand All @@ -164,7 +181,28 @@ export default function ChainParameters() {
});

if (subnetInfo.isL1 && subnetInfo.l1ValidatorManagerDetails) {
setTransparentProxyAddress(subnetInfo.l1ValidatorManagerDetails.contractAddress);
const contractAddress = subnetInfo.l1ValidatorManagerDetails.contractAddress;
setTransparentProxyAddress(contractAddress);

// Automatically check PoA owner if wallet is available
if (window.avalanche && contractAddress) {
// Create wallet client and store it in the global state
const chain = getViemL1Chain();
const walletClient = createWalletClient({
chain,
transport: custom(window.avalanche)
});
setCoreWalletClient(walletClient);

// Perform owner check immediately after setting the contract address
setTimeout(() => {
if (publicClient) {
checkPoaOwner(contractAddress, publicClient);
} else {
checkPoaOwner(contractAddress);
}
}, 100);
}
} else {
// If no contract address is found, clear any existing address
setTransparentProxyAddress('');
Expand All @@ -186,60 +224,32 @@ export default function ChainParameters() {
}
};

const handleWalletConnectionCallback = async () => {
await handleCheckPoaOwner();
const config = defineAndSaveChainConfig();
saveCoreWalletClient(config);
}

const defineAndSaveChainConfig = () => {
const chainConfig = defineChain({
id: evmChainId,
name: l1Name,
network: l1Name.toLowerCase(),
nativeCurrency: {
name: tokenSymbol,
symbol: tokenSymbol,
decimals: 18,
},
rpcUrls: {
default: { http: [rpcUrl] },
public: { http: [rpcUrl] },
},
})
setChainConfig(chainConfig)
return chainConfig;
}

const saveCoreWalletClient = (chainConfig: Chain) => {
if (!window.avalanche) return;
const noopProvider = { request: () => Promise.resolve(null) }
const provider = typeof window !== 'undefined' ? window.avalanche! : noopProvider
const walletClient = createWalletClient({
chain: chainConfig,
transport: custom(provider),
})
setCoreWalletClient(walletClient)

}

const handleCheckPoaOwner = async () => {
if (!window.ethereum) return;
const checkPoaOwner = async (contractAddress = transparentProxyAddress, client?: PublicClient) => {
if (!window.avalanche) {
console.error("Core wallet not found");
return;
}

setOwnerCheckStatus('checking');

// Get connected account from wallet
const accounts = await deduplicateEthRequestAccounts()
if (!accounts || accounts.length === 0) return;
try {
// Get connected account from Core Wallet
const accounts = await window.avalanche.request({
method: 'eth_requestAccounts'
}) as string[];

// Create public client to read contract
const publicClient = createPublicClient({
transport: http(rpcUrl)
});
if (!accounts || accounts.length === 0) {
throw new Error("No accounts found in Core Wallet");
}

// Create public client to read contract if not provided
const publicClient = client || createPublicClient({
transport: http(rpcUrl)
});

try {
// Get owner from PoA Validator Manager contract
const owner = await publicClient.readContract({
address: transparentProxyAddress as Address,
address: contractAddress as Address,
abi: PoAValidatorManagerAbi.abi,
functionName: 'owner'
}) as Address;
Expand All @@ -250,18 +260,41 @@ export default function ChainParameters() {
setOwnerCheckStatus('success');
setError("");
} else {
setError("Connected wallet is not the contract owner");
setError(`Connected wallet (${accounts[0]}) is not the contract owner (${owner})`);
setPoaOwnerAddress("undefined");
setOwnerCheckStatus('error');
}
} catch (err) {
console.error("Error checking contract owner:", err);
setError("Failed to verify contract owner");
setError("Failed to verify contract owner. Please try again.");
setPoaOwnerAddress("undefined");
setOwnerCheckStatus('error');
}
};

// Handle wallet connection
const handleWalletConnection = async () => {
try {
if (!window.avalanche) {
console.error("Core wallet not found");
return;
}

// Create wallet client and store it in the global state
const chain = getViemL1Chain();
const walletClient = createWalletClient({
chain,
transport: custom(window.avalanche)
});
setCoreWalletClient(walletClient);

// Check PoA owner after wallet connection
await checkPoaOwner();
} catch (error) {
console.error("Error connecting wallet:", error);
}
};

return (
<div className="space-y-12">
<div className="flex items-center justify-between">
Expand Down Expand Up @@ -409,22 +442,7 @@ export default function ChainParameters() {

{showWallet && (
<div className="mt-8 border-t dark:border-gray-700 pt-8">
<RequireWalletConnection
chainConfig={{
chainId: `0x${evmChainId.toString(16)}`,
chainName: l1Name,
nativeCurrency: {
name: tokenSymbol,
symbol: tokenSymbol,
decimals: 18
},
rpcUrls: [rpcUrl],
blockExplorerUrls: [],
isTestnet: true
}}
requiredBalance={0.1}
onConnection={handleWalletConnectionCallback}
/>
<RequireWalletConnection chain={getViemL1Chain()} onConnection={handleWalletConnection} />

{ownerCheckStatus === 'checking' && (
<div className="mt-4 p-4 bg-blue-50 dark:bg-blue-900/30 rounded-lg">
Expand All @@ -443,6 +461,16 @@ export default function ChainParameters() {
{ownerCheckStatus === 'error' && error && (
<div className="mt-4 p-4 bg-red-50 dark:bg-red-900/30 rounded-lg">
<p className="text-red-700 dark:text-red-300">{error}</p>
<button
onClick={() => {
setOwnerCheckStatus('none');
setError("");
checkPoaOwner();
}}
className="mt-3 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
Try Again
</button>
</div>
)}
</div>
Expand Down
Loading