diff --git a/staking-dashboard/src/components/ATPDetailsModal/ATPDetailsStakerBalance.tsx b/staking-dashboard/src/components/ATPDetailsModal/ATPDetailsStakerBalance.tsx
index 032a98b49..05dd0339e 100644
--- a/staking-dashboard/src/components/ATPDetailsModal/ATPDetailsStakerBalance.tsx
+++ b/staking-dashboard/src/components/ATPDetailsModal/ATPDetailsStakerBalance.tsx
@@ -1,46 +1,45 @@
-import { useEffect } from "react"
import { useAccount } from "wagmi"
import { formatTokenAmount } from "@/utils/atpFormatters"
import { useStakingAssetTokenDetails } from "@/hooks/stakingRegistry"
-import { useStakerBalance, useMoveFundsBackToATP } from "@/hooks/staker"
+import { useStakerBalance } from "@/hooks/staker"
import { TooltipIcon } from "@/components/Tooltip"
-import { useStakeableAmount, type ATPData } from "@/hooks"
-import { useQueryClient } from "@tanstack/react-query"
+import { Icon } from "@/components/Icon"
+import { type ATPData } from "@/hooks"
+import { useTransactionCart } from "@/contexts/TransactionCartContext"
+import { buildMoveFundsBackToATPEntry } from "@/utils/actionCart"
interface ATPDetailsStakerBalanceProps {
atp: ATPData
}
/**
- * Displays staker contract balance and provides button to move funds back to ATP
- * Only the operator can move funds back to vault
- * Compact inline layout
+ * Displays staker contract balance and a button to queue a move-funds-back-to-ATP
+ * transaction in the cart. Only the operator can submit it on-chain.
+ *
+ * Compact inline layout.
*/
export const ATPDetailsStakerBalance = ({ atp }: ATPDetailsStakerBalanceProps) => {
const { address: connectedAddress } = useAccount()
- const { balance, isLoading: isLoadingBalance, refetch } = useStakerBalance({ stakerAddress: atp.staker })
+ const { balance, isLoading: isLoadingBalance } = useStakerBalance({ stakerAddress: atp.staker })
const { symbol, decimals, isLoading: isLoadingTokenDetails } = useStakingAssetTokenDetails()
- const { moveFunds, isPending, isConfirming, isSuccess } = useMoveFundsBackToATP(atp.staker!)
- const { refetch: refetchStakeableAmount } = useStakeableAmount(atp)
- const queryClient = useQueryClient()
+ const { addTransaction, checkStepGroupInQueue, openCart } = useTransactionCart()
const isLoading = isLoadingBalance || isLoadingTokenDetails
- const isProcessing = isPending || isConfirming
const hasBalance = balance > 0n
const isOperator = connectedAddress?.toLowerCase() === atp.operator?.toLowerCase()
- // Refetch balance after successful move
- useEffect(() => {
- if (isSuccess) {
- refetch()
- refetchStakeableAmount()
+ const entry = atp.staker
+ ? buildMoveFundsBackToATPEntry({ stakerAddress: atp.staker, atpAddress: atp.atpAddress })
+ : undefined
- // Invalidate multiple stakeable amounts and refetch
- queryClient.invalidateQueries({
- queryKey: ['readContracts']
- })
- }
- }, [isSuccess, refetch])
+ const isQueued = !!entry && !!entry.metadata?.stepType && !!entry.metadata?.stepGroupIdentifier &&
+ checkStepGroupInQueue(entry.metadata.stepType, entry.metadata.stepGroupIdentifier)
+
+ const handleAddToBatch = () => {
+ if (!entry) return
+ addTransaction(entry, { preventDuplicate: true })
+ openCart()
+ }
return (
@@ -59,13 +58,24 @@ export const ATPDetailsStakerBalance = ({ atp }: ATPDetailsStakerBalanceProps) =
{hasBalance && (
-
+ {isQueued ? (
+
+ ) : (
+
+ )}
{!isOperator && (
{
const [selectedVersion, setSelectedVersion] = useState(null)
const [isOpen, setIsOpen] = useState(false)
+ const { addTransaction, checkStepGroupInQueue, openCart } = useTransactionCart()
// Get current implementation
const {
implementation: currentImplementation,
isLoading: isLoadingImplementation,
- refetch: refetchImplementation
} = useStakerImplementationFromStaker(atp.staker as Address)
// Get available versions
@@ -44,9 +45,6 @@ export const ATPDetailsStakerManagement = ({ atp }: ATPDetailsStakerManagementPr
})
const { implementations, isLoading: isLoadingImplementations } = useStakerImplementations(stakerVersions, atp.registry)
- // Staker operations
- const upgradeStakerHook = useUpgradeStaker(atp.atpAddress as Address)
-
// Get current version number
const currentVersion = useMemo(() => {
return getVersionByImplementation(currentImplementation, implementations)
@@ -67,24 +65,23 @@ export const ATPDetailsStakerManagement = ({ atp }: ATPDetailsStakerManagementPr
}
}, [selectedVersion, currentVersion, stakerVersions, isLoadingImplementation])
- // Refetch implementation after successful upgrade
- useEffect(() => {
- if (upgradeStakerHook.isSuccess) {
- refetchImplementation()
- }
- }, [upgradeStakerHook.isSuccess, refetchImplementation])
+ const upgradeEntry = useMemo(() => {
+ if (!selectedVersion) return undefined
+ return buildUpgradeStakerEntry({ atpAddress: atp.atpAddress as Address, version: selectedVersion })
+ }, [selectedVersion, atp.atpAddress])
+
+ const isUpgradeQueued = !!upgradeEntry && !!upgradeEntry.metadata?.stepType &&
+ !!upgradeEntry.metadata?.stepGroupIdentifier &&
+ checkStepGroupInQueue(upgradeEntry.metadata.stepType, upgradeEntry.metadata.stepGroupIdentifier)
const handleVersionChange = (value: string) => {
setSelectedVersion(BigInt(value))
}
- const handleUpgrade = async () => {
- if (!selectedVersion) return
- try {
- await upgradeStakerHook.upgradeStaker(selectedVersion)
- } catch (error) {
- console.error('Failed to upgrade staker:', error)
- }
+ const handleUpgrade = () => {
+ if (!upgradeEntry) return
+ addTransaction(upgradeEntry, { preventDuplicate: true })
+ openCart()
}
const isLoading = isLoadingImplementation || isLoadingImplementations
@@ -211,7 +208,6 @@ export const ATPDetailsStakerManagement = ({ atp }: ATPDetailsStakerManagementPr
{canUpgrade && (
-
+ isUpgradeQueued ? (
+
+ ) : (
+
+ )
)}
-
- {upgradeStakerHook.error && (
-
- {upgradeStakerHook.error.message.includes('rejected') ? 'Transaction cancelled' : 'Upgrade failed'}
-
- )}
-
- {upgradeStakerHook.isSuccess && (
-
- Successfully upgraded to v{selectedVersion?.toString()}
-
- )}
>
)}
diff --git a/staking-dashboard/src/components/ATPDetailsModal/ATPDetailsTechnicalInfo.tsx b/staking-dashboard/src/components/ATPDetailsModal/ATPDetailsTechnicalInfo.tsx
index 1c3d4a25a..344e7a908 100644
--- a/staking-dashboard/src/components/ATPDetailsModal/ATPDetailsTechnicalInfo.tsx
+++ b/staking-dashboard/src/components/ATPDetailsModal/ATPDetailsTechnicalInfo.tsx
@@ -1,16 +1,18 @@
-import { useState, useMemo, useEffect } from "react"
+import { useState, useMemo } from "react"
import { Icon } from "@/components/Icon"
import { useAtpRegistryData, useStakerImplementations } from "@/hooks/atpRegistry"
import { useStakerImplementation as useStakerImplementationFromStaker } from "@/hooks/staker/useStakerImplementation"
-import { useUpgradeStaker } from "@/hooks/atp"
import { AddressDisplay } from "@/components/AddressDisplay"
import { TooltipIcon } from "@/components/Tooltip"
+import { useTransactionCart } from "@/contexts/TransactionCartContext"
+import { buildUpgradeStakerEntry } from "@/utils/actionCart"
import { getVersionByImplementation, getImplementationDescription } from "@/utils/stakerVersion"
import type { ATPData } from "@/hooks/atp"
import type { Address } from "viem"
interface ATPDetailsTechnicalInfoProps {
atp: ATPData
+ // Kept for source-compatibility; cart execution drives refetch globally.
onUpgradeSuccess?: () => void
}
@@ -18,10 +20,11 @@ interface ATPDetailsTechnicalInfoProps {
* Component displaying technical details of a Token Vault position
* Shows vault address, and staker information if staker contract exists
*/
-export const ATPDetailsTechnicalInfo = ({ atp, onUpgradeSuccess }: ATPDetailsTechnicalInfoProps) => {
+export const ATPDetailsTechnicalInfo = ({ atp }: ATPDetailsTechnicalInfoProps) => {
const [isTechnicalDetailsExpanded, setIsTechnicalDetailsExpanded] = useState(true)
+ const { addTransaction, checkStepGroupInQueue, openCart } = useTransactionCart()
- const { implementation: stakerImplementation, isLoading: isLoadingImplementation, refetch } = useStakerImplementationFromStaker(
+ const { implementation: stakerImplementation, isLoading: isLoadingImplementation } = useStakerImplementationFromStaker(
atp.staker as Address
)
@@ -29,7 +32,6 @@ export const ATPDetailsTechnicalInfo = ({ atp, onUpgradeSuccess }: ATPDetailsTec
registryAddress: atp.registry
})
const { implementations, isLoading: isLoadingImplementations } = useStakerImplementations(stakerVersions, atp.registry)
- const upgradeStakerHook = useUpgradeStaker(atp.atpAddress as Address)
const stakerVersion = useMemo(() => {
return getVersionByImplementation(stakerImplementation, implementations)
@@ -47,23 +49,22 @@ export const ATPDetailsTechnicalInfo = ({ atp, onUpgradeSuccess }: ATPDetailsTec
return getImplementationDescription(stakerImplementation, stakerVersion!)
}, [stakerImplementation, stakerVersion])
- useEffect(() => {
- if (upgradeStakerHook.isSuccess) {
- refetch()
- onUpgradeSuccess?.()
- }
- }, [upgradeStakerHook.isSuccess, refetch, onUpgradeSuccess])
-
const isOnLatestVersion = stakerVersion !== null && latestVersion !== null && stakerVersion === latestVersion
const isLoadingVersion = isLoadingImplementation || isLoadingImplementations
- const handleUpgrade = async () => {
- if (!latestVersion) return
- try {
- await upgradeStakerHook.upgradeStaker(latestVersion)
- } catch (error) {
- console.error('Failed to upgrade staker:', error)
- }
+ const upgradeEntry = useMemo(() => {
+ if (!latestVersion) return undefined
+ return buildUpgradeStakerEntry({ atpAddress: atp.atpAddress as Address, version: latestVersion })
+ }, [latestVersion, atp.atpAddress])
+
+ const isUpgradeQueued = !!upgradeEntry && !!upgradeEntry.metadata?.stepType &&
+ !!upgradeEntry.metadata?.stepGroupIdentifier &&
+ checkStepGroupInQueue(upgradeEntry.metadata.stepType, upgradeEntry.metadata.stepGroupIdentifier)
+
+ const handleUpgrade = () => {
+ if (!upgradeEntry) return
+ addTransaction(upgradeEntry, { preventDuplicate: true })
+ openCart()
}
return (
@@ -130,30 +131,26 @@ export const ATPDetailsTechnicalInfo = ({ atp, onUpgradeSuccess }: ATPDetailsTec
>
) : (
<>
-
+ {isUpgradeQueued ? (
+
+ ) : (
+
+ )}
{currentDescription}
>
)}
- {upgradeStakerHook.error && (
-
- {upgradeStakerHook.error.message.includes('rejected') ? 'Transaction cancelled' : 'Upgrade failed'}
-
- )}
- {upgradeStakerHook.isSuccess && (
-
- Successfully upgraded to latest version
-
- )}
>
)}
diff --git a/staking-dashboard/src/components/ATPDetailsModal/WithdrawalActions.tsx b/staking-dashboard/src/components/ATPDetailsModal/WithdrawalActions.tsx
index 251c3c692..7f9b8f007 100644
--- a/staking-dashboard/src/components/ATPDetailsModal/WithdrawalActions.tsx
+++ b/staking-dashboard/src/components/ATPDetailsModal/WithdrawalActions.tsx
@@ -1,75 +1,15 @@
-import { useEffect } from "react";
import type { Address } from "viem";
import { useInitiateWithdraw } from "@/hooks/staker/useInitiateWithdraw";
-import { useFinalizeWithdraw } from "@/hooks/rollup/useFinalizeWithdraw";
import { TooltipIcon } from "@/components/Tooltip";
+import { Icon } from "@/components/Icon";
import { SequencerStatus } from "@/hooks/rollup/useSequencerStatus";
-import { useAlert } from "@/contexts/AlertContext";
+import { useTransactionCart } from "@/contexts/TransactionCartContext";
import { getUnlockTimeDisplay } from "@/utils/dateFormatters";
import { MilestoneStatusBadge } from "@/components/MilestoneStatusBadge";
-
-/**
- * Parse contract errors to extract user-friendly messages
- * Contract errors are often buried in the error object or masked by nonce errors
- */
-function parseContractError(error: Error): string {
- const message = error.message || "";
-
- // Known contract error signatures and their user-friendly messages
- const errorMappings: Record = {
- "Staking__NotExiting": "Sequencer is not in exiting state. Initiate unstake first.",
- "Staking__ExitDelayNotPassed": "Exit delay has not passed yet. Please wait for the withdrawal period to complete.",
- "Staking__WithdrawalDelayNotPassed": "Withdrawal delay has not passed yet. Please wait for the withdrawal period to complete.",
- "NotExiting": "Sequencer is not in exiting state.",
- "ExitDelayNotPassed": "Exit delay has not passed yet.",
- "0xef566ee0": "Exit delay has not passed yet. Please wait for the withdrawal period to complete.", // Staking__NotExiting selector
- };
-
- // Check for known error patterns
- for (const [pattern, friendlyMessage] of Object.entries(errorMappings)) {
- if (message.includes(pattern)) {
- return friendlyMessage;
- }
- }
-
- // Check for reverted errors that contain the actual reason
- const revertMatch = message.match(/reverted with.*?["']([^"']+)["']/i);
- if (revertMatch) {
- return revertMatch[1];
- }
-
- // Check for custom error data in the message
- const customErrorMatch = message.match(/error=\{[^}]*"data":"(0x[a-f0-9]+)"/i);
- if (customErrorMatch) {
- const errorData = customErrorMatch[1];
- // Check if this matches a known error selector
- for (const [selector, friendlyMessage] of Object.entries(errorMappings)) {
- if (errorData.startsWith(selector)) {
- return friendlyMessage;
- }
- }
- }
-
- // If we see nonce errors but there's also contract error data, the contract error is the real issue
- if (message.includes("nonce") && message.includes("0x")) {
- // Try to find error selector in the message
- const selectorMatch = message.match(/0x[a-f0-9]{8}/i);
- if (selectorMatch) {
- const selector = selectorMatch[0].toLowerCase();
- if (selector === "0xef566ee0") {
- return "Exit delay has not passed yet. Please wait for the withdrawal period to complete.";
- }
- }
- return "Transaction failed. The contract rejected the call - please check that all conditions are met.";
- }
-
- // Return original message if no pattern matched (but truncate if too long)
- if (message.length > 200) {
- return message.substring(0, 200) + "...";
- }
-
- return message || "Transaction failed";
-}
+import {
+ buildStakerInitiateWithdrawEntry,
+ buildRollupFinalizeWithdrawEntry,
+} from "@/utils/unstakeCart";
interface WithdrawalActionsProps {
stakerAddress: Address;
@@ -85,11 +25,16 @@ interface WithdrawalActionsProps {
atpType?: string;
registryAddress?: Address;
milestoneId?: bigint;
+ providerName?: string | null;
}
/**
- * Component for withdrawal and unstake actions
- * Displays initiate unstake and finalize withdraw buttons with proper state management
+ * Initiate / finalize unstake actions. Queues each as an `unstake` cart entry
+ * so Safe wallets batch them into a single proposal alongside any claims that
+ * happen to be in the same cart. EOA wallets get a sequential prompt per
+ * entry (unstake is `msg.sender`-bound and can't ride through Multicall3),
+ * which matches the prior immediate-tx UX from the user's perspective but
+ * persists across page reloads via the cart's localStorage state.
*/
export const WithdrawalActions = ({
stakerAddress,
@@ -101,99 +46,79 @@ export const WithdrawalActions = ({
actualUnlockTime,
withdrawalDelayDays,
onSuccess,
- // ATP context
atpType,
registryAddress,
milestoneId,
+ providerName,
}: WithdrawalActionsProps) => {
- const { showAlert } = useAlert();
const isExiting = status === SequencerStatus.EXITING;
- const {
- initiateWithdraw,
- isPending: isInitiatingWithdraw,
- isConfirming: isConfirmingInitiate,
- isSuccess: isInitiateSuccess,
- error: initiateError,
- milestoneStatus,
- isMilestoneLoading,
- canWithdraw,
- milestoneBlockError,
- } = useInitiateWithdraw(stakerAddress, {
- registryAddress,
- milestoneId,
- atpType,
- });
+ // We only consume the milestone-status read from this hook now; the
+ // immediate `initiateWithdraw(...)` call is replaced by an `addTransaction`
+ // for the cart.
+ const { milestoneStatus, isMilestoneLoading, canWithdraw, milestoneBlockError } =
+ useInitiateWithdraw(stakerAddress, {
+ registryAddress,
+ milestoneId,
+ atpType,
+ });
- const {
- finalizeWithdraw,
- isPending: isFinalizingWithdraw,
- isConfirming: isConfirmingFinalize,
- isSuccess: isFinalizeSuccess,
- error: finalizeError,
- } = useFinalizeWithdraw();
+ const { addTransaction, checkStepGroupInQueue, openCart } = useTransactionCart();
- // Determine if milestone gates operations
- const isMATP = atpType === 'MATP';
+ const isMATP = atpType === "MATP";
const isMilestoneGated = isMATP && !canWithdraw;
const canInitiateUnstake =
- (status === SequencerStatus.VALIDATING || status === SequencerStatus.ZOMBIE)
- && !isMilestoneGated; // Block if milestone not succeeded
-
- const canFinalizeWithdrawNow =
- canFinalize
- && !isMilestoneGated; // Block if milestone not succeeded
-
- // Handle initiate withdraw errors
- useEffect(() => {
- if (initiateError) {
- const errorMessage = initiateError.message;
- if (
- errorMessage.includes("User rejected") ||
- errorMessage.includes("rejected")
- ) {
- showAlert("warning", "Transaction was cancelled");
- }
- }
- }, [initiateError, showAlert]);
-
- // Handle finalize withdraw errors
- useEffect(() => {
- if (finalizeError) {
- const errorMessage = finalizeError.message;
- if (
- errorMessage.includes("User rejected") ||
- errorMessage.includes("rejected")
- ) {
- showAlert("warning", "Transaction was cancelled");
- }
- }
- }, [finalizeError, showAlert]);
-
- // Call onSuccess callback when transaction succeeds
- useEffect(() => {
- if (isInitiateSuccess || isFinalizeSuccess) {
- onSuccess?.();
- }
- }, [isInitiateSuccess, isFinalizeSuccess, onSuccess]);
-
- const handleInitiateWithdraw = async () => {
- try {
- await initiateWithdraw(rollupVersion, attesterAddress);
- } catch (error) {
- console.error("Failed to initiate withdraw:", error);
+ (status === SequencerStatus.VALIDATING || status === SequencerStatus.ZOMBIE) &&
+ !isMilestoneGated;
+ const canFinalizeWithdrawNow = canFinalize && !isMilestoneGated;
+
+ // Pre-build the cart entries used by the click handlers. We do NOT use
+ // their raw-calldata signature to detect "already queued" — that flickers
+ // when underlying data (rollup version, attester) refetches mid-render and
+ // causes duplicate cart entries. Use the stable stepGroupIdentifier from
+ // the entry's metadata instead (see `checkStepGroupInQueue`).
+ const initiateEntry = buildStakerInitiateWithdrawEntry({
+ stakerAddress,
+ version: rollupVersion,
+ attester: attesterAddress,
+ providerName,
+ });
+ const finalizeEntry = buildRollupFinalizeWithdrawEntry({
+ rollupAddress,
+ attester: attesterAddress,
+ providerName,
+ });
+ const isInitiateQueued = !!initiateEntry.metadata?.stepType
+ && !!initiateEntry.metadata?.stepGroupIdentifier
+ && checkStepGroupInQueue(initiateEntry.metadata.stepType, initiateEntry.metadata.stepGroupIdentifier);
+ const isFinalizeQueued = !!finalizeEntry.metadata?.stepType
+ && !!finalizeEntry.metadata?.stepGroupIdentifier
+ && checkStepGroupInQueue(finalizeEntry.metadata.stepType, finalizeEntry.metadata.stepGroupIdentifier);
+
+ const handleInitiateClick = () => {
+ if (isInitiateQueued) {
+ openCart();
+ return;
}
+ addTransaction(initiateEntry, { preventDuplicate: true });
+ onSuccess?.();
+ openCart();
};
- const handleFinalizeWithdraw = async () => {
- try {
- await finalizeWithdraw(attesterAddress, rollupAddress);
- } catch (error) {
- console.error("Failed to finalize withdraw:", error);
+ const handleFinalizeClick = () => {
+ if (isFinalizeQueued) {
+ openCart();
+ return;
}
+ addTransaction(finalizeEntry, { preventDuplicate: true });
+ onSuccess?.();
+ openCart();
};
+ const initiateLabel = isInitiateQueued ? "In Batch — Open Cart" : "Add Initiate Unstake";
+ const finalizeLabel = isFinalizeQueued ? "In Batch — Open Cart" : "Add Finalize Withdraw";
+
return (
@@ -201,56 +126,48 @@ export const WithdrawalActions = ({
Withdrawal Actions
- {/* Show milestone status for MATPs */}
{isMATP && (
-
+
)}
- {/* Show milestone error message */}
{milestoneBlockError && (
-
- {milestoneBlockError}
-
+
{milestoneBlockError}
)}
+
@@ -259,24 +176,26 @@ export const WithdrawalActions = ({
+
-
- {initiateError &&
- !(
- initiateError.message.includes("User rejected") ||
- initiateError.message.includes("rejected")
- ) && (
-
-
- Transaction Error
-
-
- {parseContractError(initiateError)}
-
-
- )}
-
- {finalizeError &&
- !(
- finalizeError.message.includes("User rejected") ||
- finalizeError.message.includes("rejected")
- ) && (
-
-
- Transaction Error
-
-
- {parseContractError(finalizeError)}
-
-
- )}
-
- {(isInitiateSuccess || isFinalizeSuccess) && (
-
-
- {isInitiateSuccess ? "Unstake Initiated" : "Withdrawal Finalized"}
-
-
- )}
);
};
diff --git a/staking-dashboard/src/components/AdminTools/AdminTools.tsx b/staking-dashboard/src/components/AdminTools/AdminTools.tsx
index 2998c98a1..db5607abc 100644
--- a/staking-dashboard/src/components/AdminTools/AdminTools.tsx
+++ b/staking-dashboard/src/components/AdminTools/AdminTools.tsx
@@ -2,28 +2,25 @@ import styles from "./AdminTools.module.css";
import { useState, useEffect } from "react";
import {
useAccount,
- useWriteContract,
useReadContract,
- useWaitForTransactionReceipt,
} from "wagmi";
import {
- useRegisterProvider,
useProviderRegisteredEvents,
- useProviderQueueLength,
} from "../../hooks/stakingRegistry";
import { useAtpRegistryData } from "../../hooks";
import { contracts } from "../../contracts";
+import { useTransactionCart } from "@/contexts/TransactionCartContext";
+import {
+ buildRegisterProviderEntry,
+ buildAddKeysToProviderEntry,
+ type ProviderKeyStore,
+} from "@/utils/actionCart";
import type { Address } from "viem";
export default function AdminTools() {
const { address } = useAccount();
+ const { addTransaction, openCart } = useTransactionCart();
- const registerProviderHook = useRegisterProvider();
-
- const addKeysHook = useWriteContract();
- const addKeysReceipt = useWaitForTransactionReceipt({
- hash: addKeysHook.data,
- });
const [selectedProviderId, setSelectedProviderId] = useState(1);
const { executeAllowedAt } = useAtpRegistryData();
@@ -34,9 +31,8 @@ export default function AdminTools() {
address: contracts.atpRegistry.address,
functionName: "owner",
});
- const { hasRegisteredProviders, providerCount, events, refetchEvents } =
+ const { hasRegisteredProviders, providerCount, events } =
useProviderRegisteredEvents();
- const { refetchQueueLength } = useProviderQueueLength(selectedProviderId);
// Console log the ATPRegistry owner when it changes
useEffect(() => {
@@ -75,46 +71,15 @@ export default function AdminTools() {
console.error("No address is connected thus can't set providerAdmin");
return;
}
- try {
- registerProviderHook.registerProvider(address);
- } catch (error) {
- console.log("Failed to register provider", error);
- }
+ addTransaction(
+ buildRegisterProviderEntry({ providerAdmin: address }),
+ { preventDuplicate: true },
+ );
+ openCart();
};
- useEffect(() => {
- if (registerProviderHook.isSuccess) {
- refetchEvents();
- }
- }, [registerProviderHook.isSuccess, refetchEvents]);
-
- useEffect(() => {
- if (addKeysHook.data) {
- console.log("Keys added to provider - Transaction hash:", addKeysHook.data);
- }
- if (addKeysReceipt.isSuccess) {
- console.log("Keys added to provider - Transaction confirmed on-chain");
- // Refetch queue length when key is successfully confirmed
- refetchQueueLength();
- }
- if (addKeysHook.isError || addKeysReceipt.isError) {
- console.log(
- "Keys added to provider - Transaction failed:",
- addKeysHook.error || addKeysReceipt.error,
- );
- }
- }, [
- addKeysHook.data,
- addKeysReceipt.isSuccess,
- addKeysHook.isError,
- addKeysReceipt.isError,
- addKeysHook.error,
- addKeysReceipt.error,
- refetchQueueLength,
- ]);
-
// Generate fake keystore like in tests
- const makeKeyStore = (attesterAddress: Address) => {
+ const makeKeyStore = (attesterAddress: Address): ProviderKeyStore => {
return {
attester: attesterAddress,
publicKeyG1: {
@@ -134,28 +99,20 @@ export default function AdminTools() {
};
};
- const handleAddKeysToProvider = async () => {
- try {
- // Generate a fake attester address based on provider ID
- const fakeAttester =
- `0x${selectedProviderId.toString().padStart(40, "0")}` as `0x${string}`;
- const keyStore = makeKeyStore(fakeAttester);
+ const handleAddKeysToProvider = () => {
+ // Generate a fake attester address based on provider ID
+ const fakeAttester =
+ `0x${selectedProviderId.toString().padStart(40, "0")}` as `0x${string}`;
+ const keyStore = makeKeyStore(fakeAttester);
- console.log(`Adding keys to provider ${selectedProviderId}:`, {
+ addTransaction(
+ buildAddKeysToProviderEntry({
providerId: selectedProviderId,
- attester: fakeAttester,
- keyStore,
- });
-
- addKeysHook.writeContract({
- abi: contracts.stakingRegistry.abi,
- address: contracts.stakingRegistry.address,
- functionName: "addKeysToProvider",
- args: [BigInt(selectedProviderId), [keyStore]],
- });
- } catch (error) {
- console.error("Failed to add keys to provider:", error);
- }
+ keyStores: [keyStore],
+ }),
+ { preventDuplicate: true },
+ );
+ openCart();
};
return (
@@ -183,23 +140,15 @@ export default function AdminTools() {