Skip to content

Commit f358b03

Browse files
committed
copying commits
1 parent 178258b commit f358b03

File tree

17 files changed

+2507
-0
lines changed

17 files changed

+2507
-0
lines changed

app/console/history/page.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,16 @@ export default function ConsoleHistoryPage() {
110110
type: 'address'
111111
});
112112
}
113+
if (toolboxStore.erc20StakingManagerAddress && toolboxStore.erc20StakingManagerAddress !== '') {
114+
items.push({
115+
id: 'tb-erc20-staking-mgr',
116+
title: 'ERC20 Token Staking Manager',
117+
description: 'Deployed Contract',
118+
address: toolboxStore.erc20StakingManagerAddress,
119+
chainId,
120+
type: 'address'
121+
});
122+
}
113123
if (toolboxStore.poaManagerAddress && toolboxStore.poaManagerAddress !== '') {
114124
items.push({
115125
id: 'tb-poa-mgr',
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import DelegateToValidator from '@/components/toolbox/console/permissionless-l1s/delegate/native/Delegate';
2+
3+
export default function DelegateNativeTokenPage() {
4+
return <DelegateToValidator />;
5+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"use client";
2+
3+
import StepFlow from "@/components/console/step-flow";
4+
import { steps } from "../steps";
5+
6+
export default function ERC20StakingManagerSetupClientPage({ currentStepKey }: { currentStepKey: string }) {
7+
const basePath = "/console/permissionless-l1s/erc20-staking-manager-setup";
8+
return (
9+
<StepFlow
10+
steps={steps}
11+
basePath={basePath}
12+
currentStepKey={currentStepKey}
13+
/>
14+
);
15+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import ERC20StakingManagerSetupClientPage from "./client-page";
2+
3+
export default async function Page({ params }: { params: Promise<{ step: string }> }) {
4+
const { step } = await params;
5+
return (
6+
<ERC20StakingManagerSetupClientPage currentStepKey={step} />
7+
);
8+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { redirect } from "next/navigation";
2+
3+
export default function Page() {
4+
redirect("/console/permissionless-l1s/erc20-staking-manager-setup/deploy-erc20-staking-manager");
5+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { type StepDefinition } from "@/components/console/step-flow";
2+
import ReadContract from "@/components/toolbox/console/permissioned-l1s/validator-manager-setup/ReadContract";
3+
import DeployNativeTokenStakingManager from "@/components/toolbox/console/permissionless-l1s/setup/native/DeployNativeStakingManager";
4+
import InitializeNativeTokenStakingManager from "@/components/toolbox/console/permissionless-l1s/setup/native/InitializeNativeStakingManager";
5+
import DeployERC20StakingManager from "@/components/toolbox/console/permissionless-l1s/setup/erc20/DeployERC20StakingManager";
6+
import InitializeERC20StakingManager from "@/components/toolbox/console/permissionless-l1s/setup/erc20/InitializeERC20StakingManager";
7+
import DeployExampleRewardCalculator from "@/components/toolbox/console/permissionless-l1s/setup/DeployExampleRewardCalculator";
8+
import TransferOwnership from "@/components/toolbox/console/permissioned-l1s/multisig-setup/TransferOwnership";
9+
import EnableStakingManagerMinting from "@/components/toolbox/console/permissionless-l1s/setup/native/EnableStakingManagerMinting";
10+
11+
export const steps: StepDefinition[] = [
12+
{
13+
type: "single",
14+
key: "deploy-erc20-staking-manager",
15+
title: "Deploy ERC20 Token Staking Manager",
16+
component: DeployERC20StakingManager,
17+
},
18+
{ type: "single", key: "deploy-example-reward-calculator", title: "Deploy Example Reward Calculator", component: DeployExampleRewardCalculator },
19+
{
20+
type: "single",
21+
key: "initialize-erc20-staking-manager",
22+
title: "Initialize ERC20 Token Staking Manager",
23+
component: InitializeERC20StakingManager,
24+
},
25+
{ type: "single", key: "transfer-ownership", title: "Transfer Ownership", component: TransferOwnership },
26+
{ type: "single", key: "read-contract", title: "Read Contract", component: ReadContract },
27+
];
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import Stake from "@/components/toolbox/console/permissionless-l1s/staking/native/Stake";
2+
3+
export default function Page() {
4+
return (
5+
<Stake />
6+
);
7+
}

components/console/console-sidebar.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,21 @@ const data = {
234234
url: "/console/permissionless-l1s/native-staking-manager-setup",
235235
icon: GitMerge,
236236
},
237+
{
238+
title: "ERC20 Staking Manager Setup",
239+
url: "/console/permissionless-l1s/erc20-staking-manager-setup",
240+
icon: GitMerge,
241+
},
242+
{
243+
title: "Stake Native Token",
244+
url: "/console/permissionless-l1s/stake-native-token",
245+
icon: Hexagon,
246+
},
247+
{
248+
title: "Delegate Native Token",
249+
url: "/console/permissionless-l1s/delegate-native-token",
250+
icon: HandCoins,
251+
}
237252
],
238253
},
239254
{
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import React, { useState, useEffect } from 'react';
2+
import { useWalletStore } from '@/components/toolbox/stores/walletStore';
3+
import { useViemChainStore } from '@/components/toolbox/stores/toolboxStore';
4+
import { Button } from '@/components/toolbox/components/Button';
5+
import { Input } from '@/components/toolbox/components/Input';
6+
import { Success } from '@/components/toolbox/components/Success';
7+
import { Alert } from '@/components/toolbox/components/Alert';
8+
import { bytesToHex, hexToBytes } from 'viem';
9+
import nativeTokenStakingManagerAbi from '@/contracts/icm-contracts/compiled/NativeTokenStakingManager.json';
10+
import { GetRegistrationJustification } from '@/components/toolbox/console/permissioned-l1s/ValidatorManager/justification';
11+
import { packL1ValidatorWeightMessage } from '@/components/toolbox/coreViem/utils/convertWarp';
12+
import { packWarpIntoAccessList } from '@/components/toolbox/console/permissioned-l1s/ValidatorManager/packWarp';
13+
import { useAvalancheSDKChainkit } from '@/components/toolbox/stores/useAvalancheSDKChainkit';
14+
import useConsoleNotifications from '@/hooks/useConsoleNotifications';
15+
16+
interface CompleteDelegatorRegistrationProps {
17+
subnetIdL1: string;
18+
delegationID: string;
19+
pChainTxId: string;
20+
stakingManagerAddress: string;
21+
signingSubnetId: string;
22+
onSuccess: (message: string) => void;
23+
onError: (message: string) => void;
24+
}
25+
26+
const CompleteDelegatorRegistration: React.FC<CompleteDelegatorRegistrationProps> = ({
27+
subnetIdL1,
28+
delegationID,
29+
pChainTxId,
30+
stakingManagerAddress,
31+
signingSubnetId,
32+
onSuccess,
33+
onError,
34+
}) => {
35+
const { coreWalletClient, publicClient, avalancheNetworkID, walletEVMAddress } = useWalletStore();
36+
const { aggregateSignature } = useAvalancheSDKChainkit();
37+
const { notify } = useConsoleNotifications();
38+
const viemChain = useViemChainStore();
39+
40+
const [isProcessing, setIsProcessing] = useState(false);
41+
const [error, setErrorState] = useState<string | null>(null);
42+
const [successMessage, setSuccessMessage] = useState<string | null>(null);
43+
const [transactionHash, setTransactionHash] = useState<string | null>(null);
44+
45+
const handleCompleteDelegation = async () => {
46+
setErrorState(null);
47+
setSuccessMessage(null);
48+
49+
if (!pChainTxId.trim()) {
50+
setErrorState("P-Chain transaction ID is required.");
51+
onError("P-Chain transaction ID is required.");
52+
return;
53+
}
54+
55+
if (!delegationID || delegationID === '0x0000000000000000000000000000000000000000000000000000000000000000') {
56+
setErrorState("Valid delegation ID is required.");
57+
onError("Valid delegation ID is required.");
58+
return;
59+
}
60+
61+
if (!subnetIdL1) {
62+
setErrorState("L1 Subnet ID is required. Please select a subnet first.");
63+
onError("L1 Subnet ID is required. Please select a subnet first.");
64+
return;
65+
}
66+
67+
if (!stakingManagerAddress) {
68+
setErrorState("Staking Manager address is not set. Check L1 Subnet selection.");
69+
onError("Staking Manager address is not set. Check L1 Subnet selection.");
70+
return;
71+
}
72+
73+
if (!coreWalletClient || !publicClient || !viemChain) {
74+
setErrorState("Wallet or chain configuration is not properly initialized.");
75+
onError("Wallet or chain configuration is not properly initialized.");
76+
return;
77+
}
78+
79+
setIsProcessing(true);
80+
try {
81+
// Step 1: Extract L1ValidatorWeightMessage from P-Chain transaction
82+
const weightMessageData = await coreWalletClient.extractL1ValidatorWeightMessage({
83+
txId: pChainTxId
84+
});
85+
86+
// Step 2: Get justification for the validation (using the extracted validation ID)
87+
const justification = await GetRegistrationJustification(
88+
weightMessageData.validationID,
89+
subnetIdL1,
90+
publicClient
91+
);
92+
93+
if (!justification) {
94+
throw new Error("No justification logs found for this validation ID");
95+
}
96+
97+
// Step 3: Create P-Chain warp signature using the extracted weight message data
98+
const warpValidationID = hexToBytes(weightMessageData.validationID as `0x${string}`);
99+
const warpNonce = weightMessageData.nonce;
100+
const warpWeight = weightMessageData.weight;
101+
102+
const weightMessage = packL1ValidatorWeightMessage(
103+
{
104+
validationID: warpValidationID,
105+
nonce: warpNonce,
106+
weight: warpWeight,
107+
},
108+
avalancheNetworkID,
109+
"11111111111111111111111111111111LpoYY" // always use P-Chain ID
110+
);
111+
112+
const aggregateSignaturePromise = aggregateSignature({
113+
message: bytesToHex(weightMessage),
114+
justification: bytesToHex(justification),
115+
signingSubnetId: signingSubnetId || subnetIdL1,
116+
quorumPercentage: 67,
117+
});
118+
notify({
119+
type: 'local',
120+
name: 'Aggregate Signatures'
121+
}, aggregateSignaturePromise);
122+
const signature = await aggregateSignaturePromise;
123+
124+
// Step 4: Complete the delegator registration on EVM
125+
const signedPChainWarpMsgBytes = hexToBytes(`0x${signature.signedMessage}`);
126+
const accessList = packWarpIntoAccessList(signedPChainWarpMsgBytes);
127+
128+
const writePromise = coreWalletClient.writeContract({
129+
address: stakingManagerAddress as `0x${string}`,
130+
abi: nativeTokenStakingManagerAbi.abi,
131+
functionName: "completeDelegatorRegistration",
132+
args: [delegationID as `0x${string}`, 0], // delegationID and messageIndex (0)
133+
accessList,
134+
account: walletEVMAddress as `0x${string}`,
135+
chain: viemChain,
136+
});
137+
138+
notify({
139+
type: 'call',
140+
name: 'Complete Delegator Registration'
141+
}, writePromise, viemChain ?? undefined);
142+
143+
const hash = await writePromise;
144+
const finalReceipt = await publicClient.waitForTransactionReceipt({ hash });
145+
if (finalReceipt.status !== 'success') {
146+
throw new Error(`Transaction failed with status: ${finalReceipt.status}`);
147+
}
148+
149+
setTransactionHash(hash);
150+
const successMsg = `Delegator registration completed successfully.`;
151+
setSuccessMessage(successMsg);
152+
onSuccess(successMsg);
153+
} catch (err: any) {
154+
const message = err instanceof Error ? err.message : String(err);
155+
setErrorState(`Failed to complete delegator registration: ${message}`);
156+
onError(`Failed to complete delegator registration: ${message}`);
157+
} finally {
158+
setIsProcessing(false);
159+
}
160+
};
161+
162+
// Don't render if no subnet is selected
163+
if (!subnetIdL1) {
164+
return (
165+
<div className="text-sm text-zinc-500 dark:text-zinc-400">
166+
Please select an L1 subnet first.
167+
</div>
168+
);
169+
}
170+
171+
return (
172+
<div className="space-y-4">
173+
{error && (
174+
<Alert variant="error">{error}</Alert>
175+
)}
176+
177+
<div className="text-sm text-zinc-600 dark:text-zinc-400">
178+
<p><strong>Delegation ID:</strong> {delegationID}</p>
179+
<p><strong>P-Chain Tx ID:</strong> {pChainTxId}</p>
180+
</div>
181+
182+
<Button
183+
onClick={handleCompleteDelegation}
184+
disabled={isProcessing || !!successMessage || !pChainTxId || !delegationID}
185+
>
186+
{isProcessing ? 'Processing...' : 'Sign & Complete Delegator Registration'}
187+
</Button>
188+
189+
{transactionHash && (
190+
<Success
191+
label="Transaction Hash"
192+
value={transactionHash}
193+
/>
194+
)}
195+
</div>
196+
);
197+
};
198+
199+
export default CompleteDelegatorRegistration;

0 commit comments

Comments
 (0)