Your Execution Client is not configured to store log receipts.
+
+ Please either enable log receipt storage on your current client or
+ switch to an Execution Client that supports this feature by default.
+
+ Switch your Execution client{' '}
+
+ Clients like Besu, Geth, and Nethermind store log receipts by default.
+
+ {' '}
+
+ );
+};
diff --git a/dappnode/fallbacks/ec-not-installed-page.tsx b/dappnode/fallbacks/ec-not-installed-page.tsx
new file mode 100644
index 00000000..3df0db9f
--- /dev/null
+++ b/dappnode/fallbacks/ec-not-installed-page.tsx
@@ -0,0 +1,27 @@
+import { Link } from '@lidofinance/lido-ui';
+import useDappnodeUrls from 'dappnode/hooks/use-dappnode-urls';
+import { FC } from 'react';
+import { Layout } from 'shared/layout';
+import { WelcomeSection } from './welcome-section-component';
+import { ErrorWrapper } from 'dappnode/components/text-wrappers';
+
+export const ECNotInstalledPage: FC = () => {
+ const { stakersUiUrl } = useDappnodeUrls();
+
+ return (
+
+
+
+
Execution client is not installed
+
+
+ This UI requires an execution client synced to function properly.{' '}
+
+ Please select and Execution client and wait until it's synced
+ before continuing.
+
+ Set up your node
+
+
+ );
+};
diff --git a/dappnode/fallbacks/ec-scanning-events.tsx b/dappnode/fallbacks/ec-scanning-events.tsx
new file mode 100644
index 00000000..29f2d32f
--- /dev/null
+++ b/dappnode/fallbacks/ec-scanning-events.tsx
@@ -0,0 +1,31 @@
+import { FC } from 'react';
+import { Layout } from 'shared/layout';
+import { WelcomeSection } from './welcome-section-component';
+import { WarningWrapper } from 'dappnode/components/text-wrappers';
+import { LoaderBanner } from 'shared/navigate/splash/loader-banner';
+import { Link } from '@lidofinance/lido-ui';
+import { dappnodeLidoDocsUrls } from 'dappnode/utils/dappnode-docs-urls';
+
+export const ECScanningPage: FC = () => {
+ return (
+
+
+
+
Execution client scanning blocks
+
+
To retrieve data, this UI scans blockchain events.
+
+ The first login may take a few minutes, depending on your execution
+ client.
+
+
+ If you want to reduce the waiting time, use a central RPC node (e.g.,
+ Infura).
+ Learn more in our{' '}
+ Documentation.
+
+
+
+
+ );
+};
diff --git a/dappnode/fallbacks/ec-syncing-page.tsx b/dappnode/fallbacks/ec-syncing-page.tsx
new file mode 100644
index 00000000..ac9589b3
--- /dev/null
+++ b/dappnode/fallbacks/ec-syncing-page.tsx
@@ -0,0 +1,23 @@
+import { FC } from 'react';
+import { Layout } from 'shared/layout';
+import { WelcomeSection } from './welcome-section-component';
+import { WarningWrapper } from 'dappnode/components/text-wrappers';
+import { LoaderBanner } from 'shared/navigate/splash/loader-banner';
+
+export const ECSyncingPage: FC = () => {
+ return (
+
+
+
+
Execution client is syncing
+
+
+ This UI requires an execution client synced to function properly.{' '}
+
+ Please, Wait until it's synced before continuing.
+
+ It is crucial that the keys you are about to use are not active or
+ running on any other machine. Running the same keys in multiple
+ locations can lead to conflicts, loss of funds, or security
+ vulnerabilities.
+
+
Please confirm your understanding by checking the box below:
+ Get Telegram user Id (
+
+ @userinfobot
+ {' '}
+ or{' '}
+
+ @raw_data_bot
+
+ )
+
+
+ Create and get a Telegram Bot token (
+ @BotFather)
+
+
Start the chat with your bot
+
+
+ >
+ );
+};
diff --git a/dappnode/notifications/notifications-types.tsx b/dappnode/notifications/notifications-types.tsx
new file mode 100644
index 00000000..2006a564
--- /dev/null
+++ b/dappnode/notifications/notifications-types.tsx
@@ -0,0 +1,87 @@
+import { Section } from 'shared/components';
+import { AccordionNavigatable } from 'shared/components/accordion-navigatable';
+import { NotificationsList } from './styles';
+
+const avaliableNotifications = {
+ Exits: [
+ {
+ title: 'Validator requires exit 🚨',
+ value:
+ 'One of your validators has been requested to exit. It will be done automatically',
+ },
+ {
+ title: 'Validator failed to exit, manual exit required 🚪',
+ value:
+ 'Your validator failed to exit automatically and requires manual intervention.',
+ },
+ {
+ title: 'Validator successfully exited 🚪',
+ value:
+ 'Your validator has successfully entered the exit queue without requiring manual action.',
+ },
+ ],
+ Performance: [
+ {
+ title: 'Operator in status stuck in latest report 🚨',
+ value:
+ "An operator is in a 'stuck' state for the specified epoch range. Performance should be checked.",
+ },
+ {
+ title: 'Operator bad performance in latest report 🚨',
+ value:
+ "The operator's performance was below the acceptable threshold during the specified epoch range.",
+ },
+ {
+ title: 'Operator good performance in latest report ✅',
+ value:
+ "The operator's performance exceeded the threshold during the specified epoch range.",
+ },
+ ],
+ Relays: [
+ {
+ title: 'Blacklisted relay 🚨',
+ value:
+ 'A blacklisted relay is currently being used, which is not allowed.',
+ },
+ {
+ title: 'Missing mandatory relay ⚠️',
+ value:
+ 'No mandatory relays are currently in use. Add at least one mandatory relay in the stakers UI.',
+ },
+ ],
+ Others: [
+ {
+ title: 'New distribution log updated 📦',
+ value:
+ 'A new distribution log has been updated and will be used for validator performance visualization.',
+ },
+ {
+ title: 'Execution client does not have logs receipts 🚨',
+ value:
+ 'The execution client is missing log receipts, preventing event scanning. Update your configuration or switch to a compatible client.',
+ },
+ {
+ title: 'CsModule events notifications 📋',
+ value:
+ 'Covers updates on rewards, penalties, new keys, and manager address proposals for the Lido CSModule smart contract.',
+ },
+ ],
+};
+
+export default function NotificationsTypes() {
+ return (
+
+ {Object.entries(avaliableNotifications).map(([key, value]) => (
+
+
+ {value.map((notification, i) => (
+
+ {notification.title} - {notification.value}
+
+ ))}
+
+
+ ))}
+
+ );
+}
diff --git a/dappnode/notifications/styles.ts b/dappnode/notifications/styles.ts
new file mode 100644
index 00000000..b775a627
--- /dev/null
+++ b/dappnode/notifications/styles.ts
@@ -0,0 +1,39 @@
+import styled from 'styled-components';
+import { Input } from '@lidofinance/lido-ui';
+import { TitledAddressStyle } from 'shared/components/titled-address/style';
+
+export const InfoWrapper = styled(TitledAddressStyle)`
+ padding: 22px 16px;
+ width: 100%;
+ > p {
+ font-weight: lighter;
+ font-size: 14px;
+ text-align: right;
+ }
+`;
+
+export const BotTokenWrapper = styled.div`
+ display: flex;
+ flex-direction: row;
+ gap: 12px;
+`;
+
+export const StyledInput = styled(Input)`
+ width: 100%;
+ input + span {
+ overflow: visible;
+ }
+`;
+
+export const EyeIcon = styled.div`
+ cursor: pointer;
+ display: flex;
+ margin-right: 5px;
+`;
+
+export const NotificationsList = styled.ul`
+ font-size: 14px;
+ > li {
+ margin-bottom: 20px;
+ }
+`;
diff --git a/dappnode/performance/components/performance-card.tsx b/dappnode/performance/components/performance-card.tsx
new file mode 100644
index 00000000..4f44554f
--- /dev/null
+++ b/dappnode/performance/components/performance-card.tsx
@@ -0,0 +1,37 @@
+import { Text } from '@lidofinance/lido-ui';
+import { Tooltip } from '@lidofinance/lido-ui';
+import {
+ CardTooltipWrapper,
+ PerformanceCardStyle,
+ TooltipIcon,
+} from './styles';
+
+interface PerformanceCardProps {
+ title: string;
+ tooltip?: React.ReactNode;
+ children: React.ReactNode;
+}
+
+export const PerformanceCard = ({
+ title,
+ tooltip,
+ children,
+}: PerformanceCardProps) => {
+ return (
+
+
+ {tooltip && (
+
+ ?
+
+ )}
+
+ {title}
+
+
+ {children}
+
+
+
+ );
+};
diff --git a/dappnode/performance/components/performance-table.tsx b/dappnode/performance/components/performance-table.tsx
new file mode 100644
index 00000000..cbaf2079
--- /dev/null
+++ b/dappnode/performance/components/performance-table.tsx
@@ -0,0 +1,80 @@
+import { Tbody, Td, Text, Th, Thead, Tr } from '@lidofinance/lido-ui';
+import { FC } from 'react';
+import { BeaconchainPubkeyLink } from 'shared/components';
+import { AddressRow, TableStyle, TooltipIcon } from './styles';
+import { ValidatorStats } from '../types';
+import { Tooltip } from '@lidofinance/lido-ui';
+
+interface PerformanceTableProps {
+ data: ValidatorStats[];
+ threshold: number;
+ offset?: number;
+}
+
+const tooltips = {
+ validator: "Your validator's index",
+ attestations:
+ 'Shows the number of attestations your validator has included compared to the number of attestations assigned to it for the selected range.',
+ efficiency:
+ "Shows your validator's attestation rate compared to Lido's threshold. Green indicates it's above the average; red means it's below.",
+};
+
+export const PerformanceTable: FC = ({
+ data,
+ threshold,
+}) => (
+
+
+
+
+
+
+ )}
+
+
+ >
+ );
+};
diff --git a/dappnode/performance/performance-page.tsx b/dappnode/performance/performance-page.tsx
new file mode 100644
index 00000000..3cf8af29
--- /dev/null
+++ b/dappnode/performance/performance-page.tsx
@@ -0,0 +1,49 @@
+import { FC, useState } from 'react';
+import { Faq } from 'shared/components';
+import { Layout } from 'shared/layout';
+import { useGetPerformanceByRange } from 'dappnode/hooks/use-get-performance-by-range';
+import { Range } from './types';
+
+import { PerformanceTableSection } from './performance-table-section';
+import { PerformanceChartSection } from './performance-chart-section';
+import { useAccount } from 'shared/hooks';
+import { RangeSelector } from './components/range-selector';
+import { PerformanceCardsSection } from './performance-cards-section';
+import { getConfig } from 'config';
+
+export const PerformancePage: FC = () => {
+ const { chainId } = useAccount();
+ const { defaultChain } = getConfig();
+ const [range, setRange] = useState('ever');
+ const { isLoading, validatorsStats, threshold, thresholdsByEpoch } =
+ useGetPerformanceByRange(range);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/dappnode/performance/performance-table-section.tsx b/dappnode/performance/performance-table-section.tsx
new file mode 100644
index 00000000..1411e19e
--- /dev/null
+++ b/dappnode/performance/performance-table-section.tsx
@@ -0,0 +1,33 @@
+import { FC } from 'react';
+import { WhenLoaded, Section } from 'shared/components';
+import { ViewKeysBlock } from './components/styles';
+import { PerformanceTable } from './components/performance-table';
+import { ValidatorStats } from './types';
+
+interface PerformanceTableProps {
+ isLoading: boolean;
+ validatorsStats: ValidatorStats[];
+ threshold: number;
+}
+
+export const PerformanceTableSection: FC = ({
+ isLoading,
+ validatorsStats,
+ threshold,
+}) => {
+ return (
+
+
+
+
+
+
+
+ );
+};
diff --git a/dappnode/performance/types.ts b/dappnode/performance/types.ts
new file mode 100644
index 00000000..cdaa725d
--- /dev/null
+++ b/dappnode/performance/types.ts
@@ -0,0 +1,8 @@
+export type Range = 'week' | 'month' | 'year' | 'ever';
+
+export interface ValidatorStats {
+ index: number;
+ attestations: { assigned: number; included: number };
+ // proposals: number;
+ efficiency: number;
+}
diff --git a/dappnode/starter-pack/step-wrapper.tsx b/dappnode/starter-pack/step-wrapper.tsx
new file mode 100644
index 00000000..cbd36d8c
--- /dev/null
+++ b/dappnode/starter-pack/step-wrapper.tsx
@@ -0,0 +1,14 @@
+import { FC, PropsWithChildren } from 'react';
+import { Number, StepContent, StepTitle, StepWrapper } from './styles';
+
+export const Step: FC<
+ PropsWithChildren<{ stepNum: string; title: string }>
+> = ({ stepNum: number, title, children }) => {
+ return (
+
+ {number}
+ {title}
+ {children}
+
+ );
+};
diff --git a/dappnode/starter-pack/steps.tsx b/dappnode/starter-pack/steps.tsx
new file mode 100644
index 00000000..2c28dec0
--- /dev/null
+++ b/dappnode/starter-pack/steps.tsx
@@ -0,0 +1,429 @@
+import { MATOMO_CLICK_EVENTS_TYPES } from 'consts/matomo-click-events';
+import NextLink from 'next/link';
+import {
+ Dispatch,
+ FC,
+ SetStateAction,
+ useCallback,
+ useEffect,
+ useState,
+} from 'react';
+import { MatomoLink, Note } from 'shared/components';
+import { Step2InfraRow, InfraInstalledLabel, ButtonsRow } from './styles';
+import { Step } from './step-wrapper';
+import { Button, Link } from '@lidofinance/lido-ui';
+import { CONSTANTS_BY_NETWORK } from 'consts/csm-constants';
+import { PATH } from 'consts/urls';
+import { trackMatomoEvent } from 'utils';
+import useDappnodeUrls from 'dappnode/hooks/use-dappnode-urls';
+import { InputTelegram } from 'dappnode/notifications/input-tg';
+import { dappnodeLidoDocsUrls } from 'dappnode/utils/dappnode-docs-urls';
+import isTelegramUserID from 'dappnode/utils/is-tg-user-id';
+import isTelegramBotToken from 'dappnode/utils/is-tg-bot-token';
+import usePostTelegramData from 'dappnode/hooks/use-post-telegram-data';
+import useApiBrain from 'dappnode/hooks/use-brain-keystore-api';
+import { useGetInfraStatus } from 'dappnode/hooks/use-get-infra-status';
+import useGetTelegramData from 'dappnode/hooks/use-get-telegram-data';
+import { Loader } from '@lidofinance/lido-ui';
+import { ErrorWrapper } from 'dappnode/components/text-wrappers';
+import { LoaderWrapperStyle } from 'shared/navigate/splash/loader-banner/styles';
+import { NotificationsSteps } from 'dappnode/notifications/notifications-setup-steps';
+import { CHAINS } from '@lido-sdk/constants';
+import useGetRelaysData from 'dappnode/hooks/use-get-relays-data';
+import getConfig from 'next/config';
+import { StatusTitle } from 'shared/components/status-chip/status-chip';
+
+const { publicRuntimeConfig } = getConfig();
+
+export const Steps: FC = () => {
+ const StepsTitles: Record = {
+ 1: 'Have Tokens for Bond',
+ 2: 'Set Up your node',
+ 3: 'Set up Notifications',
+ 4: 'Generate Keys',
+ };
+
+ const [step, setStep] = useState(1);
+
+ return step === 1 ? (
+
+ ) : step === 2 ? (
+
+ ) : step === 3 ? (
+
+ ) : step === 4 ? (
+
+ ) : (
+ 'Error: Please, reload the page!'
+ );
+};
+
+interface StepsProps {
+ step: number;
+ title: string;
+ setStep: Dispatch>;
+}
+
+const Step1: FC = ({ step, title, setStep }: StepsProps) => (
+ <>
+
+
+ Bond is a security collateral submitted by Node Operators
+ before uploading validator keys, covering potential losses from
+ inappropriate actions.
+
+
+
+ {' '}
+
+ It can be claimed or reused once the validator exits and any losses
+ are covered.
+
+
+
+
+ {' '}
+
+ {publicRuntimeConfig.defaultChain === 17000 ? '2 Holesky' : '2.4'}{' '}
+ ETH (stETH / wstETH equivalent) is required for the first validator{' '}
+
+ We highly recommend setting up these notifications to quickly detect
+ underperformance and avoid penalties.
+
+
+
+ You can find a guide on how to set notifications in{' '}
+
+ our Documentation
+
+ .
+
+ {postTgError && {postTgError}}
+
+
+
+
+
+ >
+ );
+};
+const Step4: FC = ({ step, title, setStep }: StepsProps) => {
+ const withdrawalByAddres =
+ CONSTANTS_BY_NETWORK[publicRuntimeConfig.defaultChain as CHAINS]
+ ?.withdrawalCredentials;
+
+ const handleClick = useCallback(() => {
+ trackMatomoEvent(MATOMO_CLICK_EVENTS_TYPES.starterPackCreateNodeOperator);
+ }, []);
+ return (
+ <>
+
+
+ In order to run a validator, you need to generate the necessary
+ keystores and deposit data.
+
+
+
+ Set {withdrawalByAddres} as the withdrawal address while
+ generating the keystores. This is the Lido Withdrawal Vault on{' '}
+ {CHAINS[publicRuntimeConfig.defaultChain as CHAINS]}
+
+
+ Prepare your deposit data (.json file) for submitting your keys in the
+ next step.
+
+
+ Just generate the keys, do NOT execute the deposits.
+
+ It is crucial that the keys you are about to use are not active or
+ running on any other machine. Running the same keys in multiple
+ locations can lead to conflicts, loss of funds, or security
+ vulnerabilities.
+
+
Please confirm your understanding by checking the box below:
Please, if Web3Signer is already installed, re-install it
+ Set Web3Signer
+
+
+
+ 0}>
+
+
+
+
+
+ {validatorsExitRequests.length}
+
+ Validator/s requested to exit
+
+
+ {validatorsExitRequests.map((val) => (
+
+
{val.index}
+
+
+ ))}
+ Exit validators
+
+
+
+
+
+
+
+
+
MEV Boost Package is not running
+
Install or restart your MEV Boost Package
+ Set MEV Boost
+
+
+
+
+
+
+
No mandatory Relays found
+
+ Select at least one of the mandatory MEV relays requested by
+ Lido
+
+ {mandatoryRelays?.map((relay, i) => (
+
{'- ' + relay.Operator}
+ ))}
+ Set up Relays
+
+
+
+
+ 0)}
+ >
+
+
+
Blacklisted Relays found
+
The following selected relays are blacklisted by Lido:
+ {usedBlacklistedRelays.map((relay, i) => (
+
+ -
+
+ ))}
+
Please, remove the Relays above from your MEV config
+ Remove blacklisted relays
+
+
+
+
+
+ );
+};
diff --git a/dappnode/utils/capitalize-first-char.ts b/dappnode/utils/capitalize-first-char.ts
new file mode 100644
index 00000000..e7e7bae0
--- /dev/null
+++ b/dappnode/utils/capitalize-first-char.ts
@@ -0,0 +1,3 @@
+export const capitalizeFirstChar = (str: string): string => {
+ return str.charAt(0).toUpperCase() + str.slice(1);
+};
diff --git a/dappnode/utils/dappnode-docs-urls.ts b/dappnode/utils/dappnode-docs-urls.ts
new file mode 100644
index 00000000..46c1c315
--- /dev/null
+++ b/dappnode/utils/dappnode-docs-urls.ts
@@ -0,0 +1,13 @@
+const baseUrl =
+ 'https://docs.dappnode.io/docs/user/staking/ethereum/lsd-pools/lido/';
+
+export const dappnodeLidoDocsUrls = {
+ register:
+ baseUrl + 'register/#first-steps-to-create-a-node-operator-in-dappnode',
+ generateKeys: baseUrl + 'register/#2-create-the-keystores--deposit-data',
+ notificationsOperatorExists:
+ baseUrl + 'already-node-operator#3-configuring-telegram-notifications',
+ notificationsNewOperator: baseUrl + 'register/#5-setup-notifications',
+ pendingHashes: baseUrl + 'performance',
+ changeRPC: baseUrl + 'overview#execution-client-rpc',
+};
diff --git a/dappnode/utils/fetchWithRetry.ts b/dappnode/utils/fetchWithRetry.ts
new file mode 100644
index 00000000..74b37784
--- /dev/null
+++ b/dappnode/utils/fetchWithRetry.ts
@@ -0,0 +1,20 @@
+export const fetchWithRetry = async (
+ url: string,
+ options: RequestInit,
+ timeout: number,
+): Promise => {
+ const shouldRetry = true;
+ while (shouldRetry) {
+ const response = await fetch(url, options);
+ if (response.status === 202) {
+ console.debug(
+ `Received status 202. Retrying in ${timeout / 1000} seconds...`,
+ );
+ await new Promise((resolve) => setTimeout(resolve, timeout));
+ } else {
+ return response;
+ }
+ }
+
+ return new Response();
+};
diff --git a/dappnode/utils/is-tg-bot-token.ts b/dappnode/utils/is-tg-bot-token.ts
new file mode 100644
index 00000000..b19edff6
--- /dev/null
+++ b/dappnode/utils/is-tg-bot-token.ts
@@ -0,0 +1,4 @@
+export default function isTelegramBotToken(token: string): boolean {
+ const telegramBotTokenPattern = /^\d{7,10}:[a-zA-Z0-9_-]{35}$/;
+ return telegramBotTokenPattern.test(token);
+}
diff --git a/dappnode/utils/is-tg-user-id.ts b/dappnode/utils/is-tg-user-id.ts
new file mode 100644
index 00000000..a6cc0e81
--- /dev/null
+++ b/dappnode/utils/is-tg-user-id.ts
@@ -0,0 +1,4 @@
+export default function isTelegramUserID(id: string): boolean {
+ const telegramUserIDPattern = /^\d+$/;
+ return telegramUserIDPattern.test(id);
+}
diff --git a/dappnode/utils/sanitize-urls.ts b/dappnode/utils/sanitize-urls.ts
new file mode 100644
index 00000000..12cdaf98
--- /dev/null
+++ b/dappnode/utils/sanitize-urls.ts
@@ -0,0 +1,26 @@
+export const sanitizeUrl = (url: string): string => {
+ if (!url) return '';
+
+ // Trim spaces
+ let sanitizedUrl = url.trim();
+
+ // Remove trailing slash unless it's just a root "/"
+ sanitizedUrl =
+ sanitizedUrl.endsWith('/') && sanitizedUrl !== '/'
+ ? sanitizedUrl.slice(0, -1)
+ : sanitizedUrl;
+
+ // Remove duplicate slashes (except in protocol)
+ sanitizedUrl = sanitizedUrl.replace(/([^:]\/)\/+/g, '$1');
+
+ // Encode special characters in the URL
+ try {
+ const urlObject = new URL(sanitizedUrl);
+ sanitizedUrl = urlObject.toString();
+ } catch (error) {
+ console.error('Invalid URL:', sanitizedUrl);
+ return '';
+ }
+
+ return sanitizedUrl;
+};
diff --git a/env-dynamics.mjs b/env-dynamics.mjs
index 0061b6e1..c8396d1b 100644
--- a/env-dynamics.mjs
+++ b/env-dynamics.mjs
@@ -18,14 +18,14 @@ const toBoolean = (dataStr) => {
/** @type string */
export const matomoHost = process.env.MATOMO_URL;
/** @type number */
-export const defaultChain = parseInt(process.env.DEFAULT_CHAIN, 10) || 17000;
+export const defaultChain = parseInt(process.env.DEFAULT_CHAIN, 10) || 560048;
/** @type number[] */
export const supportedChains = process.env?.SUPPORTED_CHAINS?.split(',').map(
(chainId) => parseInt(chainId, 10),
-) ?? [17000];
+) ?? [560048];
/** @type string */
-export const walletconnectProjectId = process.env.WALLETCONNECT_PROJECT_ID;
+export const walletconnectProjectId = 'd3f589c93f2a2de300741fcd71ed226b'; // DAPPNOPDE: process.env.WALLETCONNECT_PROJECT_ID
/** @type boolean */
export const ipfsMode = toBoolean(process.env.IPFS_MODE);
@@ -38,6 +38,10 @@ export const prefillUnsafeElRpcUrls1 =
export const prefillUnsafeElRpcUrls17000 =
process.env.PREFILL_UNSAFE_EL_RPC_URLS_17000?.split(',') ?? [];
+/** @type string[] */
+export const prefillUnsafeElRpcUrls560048 =
+ process.env.PREFILL_UNSAFE_EL_RPC_URLS_560048?.split(',') ?? [];
+
/** @type string */
export const widgetApiBasePathForIpfs =
process.env.WIDGET_API_BASE_PATH_FOR_IPFS;
diff --git a/faq/bond-1.md b/faq/holesky/bond-1.md
similarity index 100%
rename from faq/bond-1.md
rename to faq/holesky/bond-1.md
diff --git a/faq/testnet-bond-2.md b/faq/holesky/bond-2.md
similarity index 100%
rename from faq/testnet-bond-2.md
rename to faq/holesky/bond-2.md
diff --git a/faq/testnet-bond-3.md b/faq/holesky/bond-3.md
similarity index 100%
rename from faq/testnet-bond-3.md
rename to faq/holesky/bond-3.md
diff --git a/faq/bond-4.md b/faq/holesky/bond-4.md
similarity index 100%
rename from faq/bond-4.md
rename to faq/holesky/bond-4.md
diff --git a/faq/testnet-bond-5.md b/faq/holesky/bond-5.md
similarity index 100%
rename from faq/testnet-bond-5.md
rename to faq/holesky/bond-5.md
diff --git a/faq/testnet-keys-1.md b/faq/holesky/keys-1.md
similarity index 100%
rename from faq/testnet-keys-1.md
rename to faq/holesky/keys-1.md
diff --git a/faq/keys-10.md b/faq/holesky/keys-10.md
similarity index 100%
rename from faq/keys-10.md
rename to faq/holesky/keys-10.md
diff --git a/faq/keys-11.md b/faq/holesky/keys-11.md
similarity index 100%
rename from faq/keys-11.md
rename to faq/holesky/keys-11.md
diff --git a/faq/testnet-keys-12.md b/faq/holesky/keys-12.md
similarity index 100%
rename from faq/testnet-keys-12.md
rename to faq/holesky/keys-12.md
diff --git a/faq/testnet-keys-13.md b/faq/holesky/keys-13.md
similarity index 100%
rename from faq/testnet-keys-13.md
rename to faq/holesky/keys-13.md
diff --git a/faq/keys-2.md b/faq/holesky/keys-2.md
similarity index 100%
rename from faq/keys-2.md
rename to faq/holesky/keys-2.md
diff --git a/faq/testnet-keys-3.md b/faq/holesky/keys-3.md
similarity index 100%
rename from faq/testnet-keys-3.md
rename to faq/holesky/keys-3.md
diff --git a/faq/testnet-keys-3a.md b/faq/holesky/keys-3a.md
similarity index 100%
rename from faq/testnet-keys-3a.md
rename to faq/holesky/keys-3a.md
diff --git a/faq/testnet-keys-4.md b/faq/holesky/keys-4.md
similarity index 100%
rename from faq/testnet-keys-4.md
rename to faq/holesky/keys-4.md
diff --git a/faq/testnet-keys-4a.md b/faq/holesky/keys-4a.md
similarity index 100%
rename from faq/testnet-keys-4a.md
rename to faq/holesky/keys-4a.md
diff --git a/faq/keys-5.md b/faq/holesky/keys-5.md
similarity index 100%
rename from faq/keys-5.md
rename to faq/holesky/keys-5.md
diff --git a/faq/testnet-keys-6.md b/faq/holesky/keys-6.md
similarity index 100%
rename from faq/testnet-keys-6.md
rename to faq/holesky/keys-6.md
diff --git a/faq/keys-7.md b/faq/holesky/keys-7.md
similarity index 100%
rename from faq/keys-7.md
rename to faq/holesky/keys-7.md
diff --git a/faq/keys-8.md b/faq/holesky/keys-8.md
similarity index 100%
rename from faq/keys-8.md
rename to faq/holesky/keys-8.md
diff --git a/faq/keys-9.md b/faq/holesky/keys-9.md
similarity index 100%
rename from faq/keys-9.md
rename to faq/holesky/keys-9.md
diff --git a/faq/locked-1.md b/faq/holesky/locked-1.md
similarity index 100%
rename from faq/locked-1.md
rename to faq/holesky/locked-1.md
diff --git a/faq/locked-2.md b/faq/holesky/locked-2.md
similarity index 100%
rename from faq/locked-2.md
rename to faq/holesky/locked-2.md
diff --git a/faq/locked-3.md b/faq/holesky/locked-3.md
similarity index 100%
rename from faq/locked-3.md
rename to faq/holesky/locked-3.md
diff --git a/faq/main-1.md b/faq/holesky/main-1.md
similarity index 100%
rename from faq/main-1.md
rename to faq/holesky/main-1.md
diff --git a/faq/main-2.md b/faq/holesky/main-2.md
similarity index 100%
rename from faq/main-2.md
rename to faq/holesky/main-2.md
diff --git a/faq/main-3.md b/faq/holesky/main-3.md
similarity index 100%
rename from faq/main-3.md
rename to faq/holesky/main-3.md
diff --git a/faq/main-4.md b/faq/holesky/main-4.md
similarity index 100%
rename from faq/main-4.md
rename to faq/holesky/main-4.md
diff --git a/faq/main-5.md b/faq/holesky/main-5.md
similarity index 100%
rename from faq/main-5.md
rename to faq/holesky/main-5.md
diff --git a/faq/main-6.md b/faq/holesky/main-6.md
similarity index 100%
rename from faq/main-6.md
rename to faq/holesky/main-6.md
diff --git a/faq/testnet-main-7.md b/faq/holesky/main-7.md
similarity index 100%
rename from faq/testnet-main-7.md
rename to faq/holesky/main-7.md
diff --git a/faq/testnet-main-7a.md b/faq/holesky/main-7a.md
similarity index 100%
rename from faq/testnet-main-7a.md
rename to faq/holesky/main-7a.md
diff --git a/faq/testnet-main-8.md b/faq/holesky/main-8.md
similarity index 100%
rename from faq/testnet-main-8.md
rename to faq/holesky/main-8.md
diff --git a/faq/roles-1.md b/faq/holesky/roles-1.md
similarity index 100%
rename from faq/roles-1.md
rename to faq/holesky/roles-1.md
diff --git a/faq/roles-2.md b/faq/holesky/roles-2.md
similarity index 100%
rename from faq/roles-2.md
rename to faq/holesky/roles-2.md
diff --git a/faq/roles-3.md b/faq/holesky/roles-3.md
similarity index 100%
rename from faq/roles-3.md
rename to faq/holesky/roles-3.md
diff --git a/faq/roles-4.md b/faq/holesky/roles-4.md
similarity index 100%
rename from faq/roles-4.md
rename to faq/holesky/roles-4.md
diff --git a/faq/roles-5.md b/faq/holesky/roles-5.md
similarity index 100%
rename from faq/roles-5.md
rename to faq/holesky/roles-5.md
diff --git a/faq/testnet-bond-1.md b/faq/hoodi/bond-1.md
similarity index 100%
rename from faq/testnet-bond-1.md
rename to faq/hoodi/bond-1.md
diff --git a/faq/hoodi/bond-2.md b/faq/hoodi/bond-2.md
new file mode 100644
index 00000000..1697d259
--- /dev/null
+++ b/faq/hoodi/bond-2.md
@@ -0,0 +1,7 @@
+---
+title: How often do I get rewards?
+---
+
+**Node Operator rewards** on testnet are calculated and made claimable by the CSM Oracle **every 7 days**. Rewards do not have to be claimed during every reporting frame, and can be left to accumulate to be claimed later.
+
+**Bond rebase part** of the rewards come from stETH being a rebasing token and the bond being stored in stETH. After each Accounting Oracle report that happens on testnet **every 12 epochs (1hr 20min)**, the share rate changes. Hence, the same amount of stETH shares will now be equal to a bigger stETH token balance.
diff --git a/faq/hoodi/bond-3.md b/faq/hoodi/bond-3.md
new file mode 100644
index 00000000..de49ca7e
--- /dev/null
+++ b/faq/hoodi/bond-3.md
@@ -0,0 +1,9 @@
+---
+title: Why didn’t I get rewards?
+anchor: why-did-not-i-get-rewards
+---
+
+There are two main reasons of you getting no reward within a frame:
+
+1. If your validator’s performance was below the threshold within the CSM Performance Oracle frame (7 days for testnet) the validator does not receive rewards for the given frame. Read more about [the CSM Performance Oracle](https://operatorportal.lido.fi/modules/community-staking-module#block-c6dc8d00f13243fcb17de3fa07ecc52c).
+2. [Your Node Operator has stuck keys](https://operatorportal.lido.fi/modules/community-staking-module#block-0ed61a4c0a5a439bbb4be20e814b4e38) due to not exiting a validator requested for exit timely.
diff --git a/faq/testnet-bond-4.md b/faq/hoodi/bond-4.md
similarity index 100%
rename from faq/testnet-bond-4.md
rename to faq/hoodi/bond-4.md
diff --git a/faq/hoodi/bond-5.md b/faq/hoodi/bond-5.md
new file mode 100644
index 00000000..90af7696
--- /dev/null
+++ b/faq/hoodi/bond-5.md
@@ -0,0 +1,13 @@
+---
+title: How to claim ETH using a withdrawal NFT
+anchor: how-to-claim-eth
+---
+
+Claiming bond and rewards in a form of ETH constitutes an stETH withdrawal process (unstake).
+
+The withdrawal process consists of several steps you need to do:
+
+- **Submit a withdrawal request** by choosing ETH as a token for bond/rewards claim. As a result of this step, you will receive a withdrawal NFT.
+- **Claim your ETH** after request fulfilment. The fulfilment process takes 1-5 days (or longer), [depending on a variety of factors](https://help.lido.fi/en/articles/7858315-how-long-does-an-ethereum-withdrawal-take). To know if your ETH is ready to be claimed you, can check its status on the [Claim page](https://stake-holesky.testnet.fi/withdrawals/claim). If your request is marked as “**Ready to claim**”, it is time for you to get your ETH back.
+
+For more information about withdrawals, [follow the page](https://help.lido.fi/en/collections/3993867-ethereum-withdrawals).
diff --git a/faq/hoodi/keys-1.md b/faq/hoodi/keys-1.md
new file mode 100644
index 00000000..08ec2b0e
--- /dev/null
+++ b/faq/hoodi/keys-1.md
@@ -0,0 +1,13 @@
+---
+title: How to set up a validator for CSM testnet?
+---
+
+A detailed guide on preparing all the validation tools for CSM can be found [here](https://dvt-homestaker.stakesaurus.com/bonded-validators-setup/lido-csm).
+
+A shorter flow of setting up a CSM validator for **testnet** looks as follows:
+
+1. [Generate new validator keys](https://dvt-homestaker.stakesaurus.com/keystore-generation-and-mev-boost/validator-key-generation) setting the `withdrawal_address` to the [Lido Withdrawal Vault](https://hoodi.cloud.blockscout.com/address/0x4473dCDDbf77679A643BdB654dbd86D67F8d32f2) on **Hoodi:** [`0x4473dCDDbf77679A643BdB654dbd86D67F8d32f2`](https://hoodi.cloud.blockscout.com/address/0x4473dCDDbf77679A643BdB654dbd86D67F8d32f2) and specify the deposit amount of 32 ETH (do **NOT** make a deposit)
+2. [Configure your validator client](https://dvt-homestaker.stakesaurus.com/native-solo-staking-setup/validator-client-setup) (and/or beacon node) setting the `fee_recipient` flag to the designated fee recipient address (Lido Execution Layer Rewards Vault) on **Hoodi:** [`0x9b108015fe433F173696Af3Aa0CF7CDb3E104258`](https://hoodi.cloud.blockscout.com/address/0x9b108015fe433F173696Af3Aa0CF7CDb3E104258) and import the newly generated CSM keystores
+3. **Do not setup any MEV-Boost**
+4. [Upload the newly generated deposit data](https://dvt-homestaker.stakesaurus.com/bonded-validators-setup/lido-csm/upload-remove-view-validator-keys) file pertaining to your CSM keystores onto [the Lido CSM Widget](https://csm.testnet.fi/) and provide the required bond amount in Hoodi ETH/stETH/wstETH. Before uploading, make sure that nodes are synced, running, and ready for the validator activation.
+5. Wait for your CSM validator keys to be deposited through the protocol and make sure your node remains online in the meantime!
diff --git a/faq/testnet-keys-10.md b/faq/hoodi/keys-10.md
similarity index 100%
rename from faq/testnet-keys-10.md
rename to faq/hoodi/keys-10.md
diff --git a/faq/testnet-keys-11.md b/faq/hoodi/keys-11.md
similarity index 100%
rename from faq/testnet-keys-11.md
rename to faq/hoodi/keys-11.md
diff --git a/faq/hoodi/keys-12.md b/faq/hoodi/keys-12.md
new file mode 100644
index 00000000..1b15238d
--- /dev/null
+++ b/faq/hoodi/keys-12.md
@@ -0,0 +1,5 @@
+---
+title: What to do in case of technical issues?
+---
+
+For community assistance, join the "[CSM-testnet](https://discord.com/channels/761182643269795850/1255114351120089148)" channel on the [Lido Discord server](https://discord.com/invite/lido) to seek advice and guidance.
diff --git a/faq/hoodi/keys-13.md b/faq/hoodi/keys-13.md
new file mode 100644
index 00000000..2dd0f972
--- /dev/null
+++ b/faq/hoodi/keys-13.md
@@ -0,0 +1,18 @@
+---
+title: What is the CSM Stake Share Limit?
+anchor: stake-share-limit
+---
+
+The stake share limit is a parameter defined for each Staking Module based on its risk profile. It determines the percentage of the total stake in the Lido Protocol that can be allocated to the module. Currently, the stake share limit for CSM is set at 15%. Once CSM reaches its stake share limit, new keys can still be uploaded, but deposits to these keys may take a very long time (e.g. months), if they are deposited to at all. These factors affect the possibility of new deposits to your uploaded keys:
+
+- The number of keys already in the deposit queue, and the position of your keys in this queue
+- The number of keys that will exit from CSM
+- Changes in the total volume of stake in the Lido Protocol (both net flows as well as whether overall Lido protocol stake increases or not)
+
+In other words, if keys had not been deposited to before CSM reached its limit, they may still be deposited to later if:
+
+- The overall stake volume in the Lido Protocol increases
+- Keys exit from CSM, freeing up space for new keys
+- The DAO decides to increase CSM's stake share limit
+
+While keys are awaiting deposit, Node Operators continue to receive daily bond rewards based on the bond they submitted. However, they do not receive Node Operator rewards, as the keys remain inactive until they are fully deposited.
diff --git a/faq/testnet-keys-2.md b/faq/hoodi/keys-2.md
similarity index 100%
rename from faq/testnet-keys-2.md
rename to faq/hoodi/keys-2.md
diff --git a/faq/hoodi/keys-3.md b/faq/hoodi/keys-3.md
new file mode 100644
index 00000000..dd1013fa
--- /dev/null
+++ b/faq/hoodi/keys-3.md
@@ -0,0 +1,13 @@
+---
+title: How much bond is needed?
+earlyAdoption: false
+anchor: how-bond-is-calculated
+---
+
+The initial bond requirement for the first validator for the Hoodi testnet is 2.4 stETH. However, for [Early Adopters](https://operatorportal.lido.fi/modules/community-staking-module#block-ef60a1fa96ae4c7995dd7794de2a3e22), this amount is reduced to 1.5 stETH to incentivize early participation.
+
+The amount for the second and subsequent validators is 1.3 stETH
+
+For the Hoodi testnet, the values for the bond curve are the following:
+
+
diff --git a/faq/hoodi/keys-3a.md b/faq/hoodi/keys-3a.md
new file mode 100644
index 00000000..5c7af749
--- /dev/null
+++ b/faq/hoodi/keys-3a.md
@@ -0,0 +1,13 @@
+---
+title: How much bond is needed?
+earlyAdoption: true
+anchor: how-bond-is-calculated
+---
+
+The initial bond requirement for the first validator for the Hoodi testnet is 2.4 stETH. However, for [Early Adopters](https://operatorportal.lido.fi/modules/community-staking-module#block-ef60a1fa96ae4c7995dd7794de2a3e22), this amount is reduced to 1.5 stETH to incentivize early participation.
+
+The amount for the second and subsequent validators is 1.3 stETH
+
+For the Hoodi testnet, the values for the bond curve are the following:
+
+
diff --git a/faq/hoodi/keys-4.md b/faq/hoodi/keys-4.md
new file mode 100644
index 00000000..663b9fd4
--- /dev/null
+++ b/faq/hoodi/keys-4.md
@@ -0,0 +1,10 @@
+---
+title: What is the bond curve?
+earlyAdoption: false
+---
+
+[The bond curve](https://operatorportal.lido.fi/modules/community-staking-module#block-2d1c307d95fc4f8ab7c32b7584f795cf) is a function that determines the amount of bond required for each subsequent validator operated by the node operator. For [Early Adopters](https://operatorportal.lido.fi/modules/community-staking-module#block-ef60a1fa96ae4c7995dd7794de2a3e22), a unique bond curve function is applied to incentivize early participation.
+
+For the Hoodi testnet, the values for the bond curve are the following:
+
+
diff --git a/faq/hoodi/keys-4a.md b/faq/hoodi/keys-4a.md
new file mode 100644
index 00000000..810697a1
--- /dev/null
+++ b/faq/hoodi/keys-4a.md
@@ -0,0 +1,10 @@
+---
+title: What is the bond curve?
+earlyAdoption: true
+---
+
+[The bond curve](https://operatorportal.lido.fi/modules/community-staking-module#block-2d1c307d95fc4f8ab7c32b7584f795cf) is a function that determines the amount of bond required for each subsequent validator operated by the node operator. For [Early Adopters](https://operatorportal.lido.fi/modules/community-staking-module#block-ef60a1fa96ae4c7995dd7794de2a3e22), a unique bond curve function is applied to incentivize early participation.
+
+For the Hoodi testnet, the values for the bond curve are the following:
+
+
diff --git a/faq/testnet-keys-5.md b/faq/hoodi/keys-5.md
similarity index 100%
rename from faq/testnet-keys-5.md
rename to faq/hoodi/keys-5.md
diff --git a/faq/hoodi/keys-6.md b/faq/hoodi/keys-6.md
new file mode 100644
index 00000000..0c2bddda
--- /dev/null
+++ b/faq/hoodi/keys-6.md
@@ -0,0 +1,16 @@
+---
+title: When does a validator become active?
+anchor: when-validator-become-active
+---
+
+After key submission, and if keys have been successfully validated, two actions are required for a validator to be activated:
+
+1. **Deposit by Lido Protocol**: The time to deposit a validator is unpredictable and depends on factors such as total stake inflows and outflows, gas considerations, module shares, CSM deposit queue size, and the Node Operator's place in the queue.
+
+ You can subscribe to [the important CSM events](https://docs.lido.fi/staking-modules/csm/guides/events) to stay notified about your validator being deposited to.
+
+ Read more information about the deposits flow [here](https://operatorportal.lido.fi/modules/community-staking-module#block-90b8ff95edc64cf7a051584820219616).
+
+2. **Activation on Ethereum Network**: Once deposited, the validator enters the Beacon Chain activation queue. The time to activation depends on the total number of validators in the queue awaiting activation and the rate of queue processing, which varies based on the total number of active Ethereum validators.
+
+ You can check if the keys are activated on the [Keys tab](https://csm.testnet.fi/keys) or on [beaconcha.in](http://beaconcha.in/)
diff --git a/faq/testnet-keys-7.md b/faq/hoodi/keys-7.md
similarity index 100%
rename from faq/testnet-keys-7.md
rename to faq/hoodi/keys-7.md
diff --git a/faq/testnet-keys-8.md b/faq/hoodi/keys-8.md
similarity index 100%
rename from faq/testnet-keys-8.md
rename to faq/hoodi/keys-8.md
diff --git a/faq/testnet-keys-9.md b/faq/hoodi/keys-9.md
similarity index 100%
rename from faq/testnet-keys-9.md
rename to faq/hoodi/keys-9.md
diff --git a/faq/testnet-locked-1.md b/faq/hoodi/locked-1.md
similarity index 100%
rename from faq/testnet-locked-1.md
rename to faq/hoodi/locked-1.md
diff --git a/faq/testnet-locked-2.md b/faq/hoodi/locked-2.md
similarity index 100%
rename from faq/testnet-locked-2.md
rename to faq/hoodi/locked-2.md
diff --git a/faq/testnet-locked-3.md b/faq/hoodi/locked-3.md
similarity index 100%
rename from faq/testnet-locked-3.md
rename to faq/hoodi/locked-3.md
diff --git a/faq/testnet-main-1.md b/faq/hoodi/main-1.md
similarity index 100%
rename from faq/testnet-main-1.md
rename to faq/hoodi/main-1.md
diff --git a/faq/testnet-main-2.md b/faq/hoodi/main-2.md
similarity index 100%
rename from faq/testnet-main-2.md
rename to faq/hoodi/main-2.md
diff --git a/faq/testnet-main-3.md b/faq/hoodi/main-3.md
similarity index 100%
rename from faq/testnet-main-3.md
rename to faq/hoodi/main-3.md
diff --git a/faq/testnet-main-4.md b/faq/hoodi/main-4.md
similarity index 100%
rename from faq/testnet-main-4.md
rename to faq/hoodi/main-4.md
diff --git a/faq/testnet-main-5.md b/faq/hoodi/main-5.md
similarity index 100%
rename from faq/testnet-main-5.md
rename to faq/hoodi/main-5.md
diff --git a/faq/testnet-main-6.md b/faq/hoodi/main-6.md
similarity index 100%
rename from faq/testnet-main-6.md
rename to faq/hoodi/main-6.md
diff --git a/faq/hoodi/main-7.md b/faq/hoodi/main-7.md
new file mode 100644
index 00000000..f1e10d2c
--- /dev/null
+++ b/faq/hoodi/main-7.md
@@ -0,0 +1,12 @@
+---
+title: How much bond is needed?
+earlyAdoption: false
+---
+
+The initial bond requirement for the first validator for the testnet is 2 stETH. However, for [Early Adopters](https://operatorportal.lido.fi/modules/community-staking-module#block-ef60a1fa96ae4c7995dd7794de2a3e22), this amount is reduced to 1.5 stETH to incentivize early participation.
+
+Subsequent bond amounts depend on the total number of validators operated by the node operator and follow a specific function known as the “[bond curve](https://operatorportal.lido.fi/modules/community-staking-module#block-2d1c307d95fc4f8ab7c32b7584f795cf)”, which adjusts the bond requirement based on the operator's validator count.
+
+For the testnet, the values for the bond curve are the following:
+
+
diff --git a/faq/hoodi/main-7a.md b/faq/hoodi/main-7a.md
new file mode 100644
index 00000000..fd008e98
--- /dev/null
+++ b/faq/hoodi/main-7a.md
@@ -0,0 +1,12 @@
+---
+title: How much bond is needed?
+earlyAdoption: true
+---
+
+The initial bond requirement for the first validator for the testnet is 2 stETH. However, for [Early Adopters](https://operatorportal.lido.fi/modules/community-staking-module#block-ef60a1fa96ae4c7995dd7794de2a3e22), this amount is reduced to 1.5 stETH to incentivize early participation.
+
+Subsequent bond amounts depend on the total number of validators operated by the node operator and follow a specific function known as the “[bond curve](https://operatorportal.lido.fi/modules/community-staking-module#block-2d1c307d95fc4f8ab7c32b7584f795cf)”, which adjusts the bond requirement based on the operator's validator count.
+
+For the testnet, the values for the bond curve are the following:
+
+
diff --git a/faq/hoodi/main-8.md b/faq/hoodi/main-8.md
new file mode 100644
index 00000000..35c06f54
--- /dev/null
+++ b/faq/hoodi/main-8.md
@@ -0,0 +1,5 @@
+---
+title: How can I get help?
+---
+
+For community assistance, join the "[CSM-testnet](https://discord.com/channels/761182643269795850/1255114351120089148)" channel on the [Lido Discord server](https://discord.com/invite/lido) to seek advice and guidance.
diff --git a/faq/testnet-roles-1.md b/faq/hoodi/roles-1.md
similarity index 100%
rename from faq/testnet-roles-1.md
rename to faq/hoodi/roles-1.md
diff --git a/faq/testnet-roles-2.md b/faq/hoodi/roles-2.md
similarity index 100%
rename from faq/testnet-roles-2.md
rename to faq/hoodi/roles-2.md
diff --git a/faq/testnet-roles-3.md b/faq/hoodi/roles-3.md
similarity index 100%
rename from faq/testnet-roles-3.md
rename to faq/hoodi/roles-3.md
diff --git a/faq/testnet-roles-4.md b/faq/hoodi/roles-4.md
similarity index 100%
rename from faq/testnet-roles-4.md
rename to faq/hoodi/roles-4.md
diff --git a/faq/testnet-roles-5.md b/faq/hoodi/roles-5.md
similarity index 100%
rename from faq/testnet-roles-5.md
rename to faq/hoodi/roles-5.md
diff --git a/faq/mainnet/bond-1.md b/faq/mainnet/bond-1.md
new file mode 100644
index 00000000..f4cc4ef4
--- /dev/null
+++ b/faq/mainnet/bond-1.md
@@ -0,0 +1,8 @@
+---
+title: What rewards do I get in CSM?
+---
+
+When CSM operators use the Lido protocol to run validators, they can receive two types of rewards:
+
+- **Node Operator rewards**: a share of rewards from staker locked stake amount, currently calculated pro-rata based on validators operated as a share of total protocol validators, with [possible reductions for bad performance](https://operatorportal.lido.fi/modules/community-staking-module#block-c6dc8d00f13243fcb17de3fa07ecc52c).
+- **Bond rebase**: staking rewards pertaining to the bonded tokens (stETH).
diff --git a/faq/bond-2.md b/faq/mainnet/bond-2.md
similarity index 100%
rename from faq/bond-2.md
rename to faq/mainnet/bond-2.md
diff --git a/faq/bond-3.md b/faq/mainnet/bond-3.md
similarity index 100%
rename from faq/bond-3.md
rename to faq/mainnet/bond-3.md
diff --git a/faq/mainnet/bond-4.md b/faq/mainnet/bond-4.md
new file mode 100644
index 00000000..4991f37c
--- /dev/null
+++ b/faq/mainnet/bond-4.md
@@ -0,0 +1,9 @@
+---
+title: Why add a bond?
+---
+
+Adding bond is a prevention measure to avoid Exit Request for your validators if they became unbonded. [Unbonded validators](https://docs.lido.fi/staking-modules/csm/guides/unbonded-validators) appear if the Node Operator's bond is no longer sufficient to cover all the validator keys uploaded to CSM by the Node Operator.
+
+If a [penalty](https://operatorportal.lido.fi/modules/community-staking-module#block-3951aa72ba1e471bafe95b40fef65d2b) was already applied, there is a relatively short period of time until the next VEBO report, which most likely will contain a validator Exit Request. During this period in between penalty application and the next VEBO report, Node Operators must top up bond to avoid Exit Requests for their validator(s).
+
+**Warning:** If the unbonded validator has already been requested to exit, Node Operators can only exit it. The bond top-up after the Exit Request will not reverse the Exit Request.
diff --git a/faq/bond-5.md b/faq/mainnet/bond-5.md
similarity index 100%
rename from faq/bond-5.md
rename to faq/mainnet/bond-5.md
diff --git a/faq/keys-1.md b/faq/mainnet/keys-1.md
similarity index 100%
rename from faq/keys-1.md
rename to faq/mainnet/keys-1.md
diff --git a/faq/mainnet/keys-10.md b/faq/mainnet/keys-10.md
new file mode 100644
index 00000000..978d3d64
--- /dev/null
+++ b/faq/mainnet/keys-10.md
@@ -0,0 +1,10 @@
+---
+title: When does a validator become withdrawn?
+anchor: when-validator-become-withdrawn
+---
+
+On the Ethereum network, a validator can be withdrawn after successfully exiting from the consensus layer, but the exact timing of withdrawal depends on several factors related to Ethereum protocol mechanics:
+
+1. **Exit Queue**: When a validator initiates an exit, it enters an exit queue. The time required to exit depends on the number of validators exiting and the churn limit (the number of validators allowed to exit or enter per epoch).
+2. **Withdrawal Process**: After exiting the active validator set, the validator enters a withdrawable state. This state is determined by the withdrawable epoch, which is set to the exit epoch + a minimum delay of 256 epochs (~27 hours).
+3. **Finalization of Withdrawal**: Once the withdrawable epoch is reached, the validator balance will be transferred to the validator's withdrawal credentials (in the case of the Lido protocol, the Lido Withdrawal Vault) within the next iteration of the Consensus Layer withdrawal sweep cycle. How long this takes depends on the validator's position in the overall sweep cycle, and time difference between the withdrawable epoch and when its turn will come to be swept. Once the withdrawal has occurred, the fact of the withdrawal can be reported to CSM permissionlessly. Once that occurs, the part of the Node Operator’s bond used for this validator is released. At this point, the Node Operator can claim the bond on the Bond & Rewards Claim tab.
diff --git a/faq/mainnet/keys-11.md b/faq/mainnet/keys-11.md
new file mode 100644
index 00000000..88e7c56c
--- /dev/null
+++ b/faq/mainnet/keys-11.md
@@ -0,0 +1,6 @@
+---
+title: What is a referrer?
+onlyWithReferrer: true
+---
+
+A referrer is a software provider specializing in node/validator setup that integrated CSM into their tools. When a referrer directs solo stakers to join CSM via its tool, these Node Operators are marked as being referred from this provider. It doesn’t affect the Node Operators rewards in any way and is used just for the funnel-tracking purposes.
diff --git a/faq/keys-12.md b/faq/mainnet/keys-12.md
similarity index 100%
rename from faq/keys-12.md
rename to faq/mainnet/keys-12.md
diff --git a/faq/keys-13.md b/faq/mainnet/keys-13.md
similarity index 100%
rename from faq/keys-13.md
rename to faq/mainnet/keys-13.md
diff --git a/faq/mainnet/keys-2.md b/faq/mainnet/keys-2.md
new file mode 100644
index 00000000..fa478fde
--- /dev/null
+++ b/faq/mainnet/keys-2.md
@@ -0,0 +1,11 @@
+---
+title: Why upload a bond?
+---
+
+Submitting a bond serves as a risk mitigation measure for both the Ethereum network and the Lido protocol.
+
+There are several major reasons for a CSM Node Operator's bond to be penalized, including:
+
+- **The validator has been slashed.** In this case, the initial (minimal) slashing penalty is confiscated. `Penalty amount` = `1 ETH (EFFECTIVE_BALANCE / 32)`;
+- **The operator has stolen EL rewards (MEV)**. `Penalty amount` = `amount stolen` + `fixed stealing fine`;
+- **The validator's withdrawal balance is less than 32 ETH**. `Penalty amount` = `32` - `validator's withdrawal balance.`
diff --git a/faq/keys-3.md b/faq/mainnet/keys-3.md
similarity index 100%
rename from faq/keys-3.md
rename to faq/mainnet/keys-3.md
diff --git a/faq/keys-3a.md b/faq/mainnet/keys-3a.md
similarity index 100%
rename from faq/keys-3a.md
rename to faq/mainnet/keys-3a.md
diff --git a/faq/keys-4.md b/faq/mainnet/keys-4.md
similarity index 100%
rename from faq/keys-4.md
rename to faq/mainnet/keys-4.md
diff --git a/faq/keys-4a.md b/faq/mainnet/keys-4a.md
similarity index 100%
rename from faq/keys-4a.md
rename to faq/mainnet/keys-4a.md
diff --git a/faq/mainnet/keys-5.md b/faq/mainnet/keys-5.md
new file mode 100644
index 00000000..8539e45a
--- /dev/null
+++ b/faq/mainnet/keys-5.md
@@ -0,0 +1,5 @@
+---
+title: Difference between bond types (ETH, stETH, wstETH)?
+---
+
+Bonds are stored in the form of stETH to so that participation as a Node Operator is more capital efficient than if the bond were un-staked (or could only be staked if sufficient deposits to fill the submitted validators were present). While node operators can submit bond in ETH, stETH, or wstETH, any token other than stETH is converted to stETH for consistency in bond format.
diff --git a/faq/keys-6.md b/faq/mainnet/keys-6.md
similarity index 100%
rename from faq/keys-6.md
rename to faq/mainnet/keys-6.md
diff --git a/faq/mainnet/keys-7.md b/faq/mainnet/keys-7.md
new file mode 100644
index 00000000..2049a4e0
--- /dev/null
+++ b/faq/mainnet/keys-7.md
@@ -0,0 +1,5 @@
+---
+title: Why pay for key deletion?
+---
+
+Key deletion incurs a removal fee of 0.05 ETH, which is deducted from the Node Operator's bond per each deleted key to cover the maximal possible operational costs associated with the queue processing. This fee is intended to protect the module from potential DoS attacks by malicious actors who could clog the queue with empty slots by adding and removing keys, and covers the maximal possible operational costs associated with the queue processing. The fee discourages misuse, keeping the system clear of invalid keys or keys that don’t end up being deposited to.
diff --git a/faq/mainnet/keys-8.md b/faq/mainnet/keys-8.md
new file mode 100644
index 00000000..79c65c68
--- /dev/null
+++ b/faq/mainnet/keys-8.md
@@ -0,0 +1,5 @@
+---
+title: Can't see the key for deletion?
+---
+
+Only keys that have not been deposited yet can be deleted. If a key has already been deposited, the only way to retrieve the bond is [to exit the validator on the Consensus Layer (CL)](https://dvt-homestaker.stakesaurus.com/bonded-validators-setup/lido-csm/exiting-csm-validators). Once withdrawn, the node operator can claim the excess bond.
diff --git a/faq/mainnet/keys-9.md b/faq/mainnet/keys-9.md
new file mode 100644
index 00000000..c1361d38
--- /dev/null
+++ b/faq/mainnet/keys-9.md
@@ -0,0 +1,5 @@
+---
+title: How to stop validating in CSM?
+---
+
+Exiting CSM validator keys works the same way as exiting solo staking validator keys. The guide on how to exit the validator can be found [here](https://dvt-homestaker.stakesaurus.com/bonded-validators-setup/lido-csm/exiting-csm-validators#manual-exit).
diff --git a/faq/mainnet/locked-1.md b/faq/mainnet/locked-1.md
new file mode 100644
index 00000000..ea31fa37
--- /dev/null
+++ b/faq/mainnet/locked-1.md
@@ -0,0 +1,5 @@
+---
+title: Why is the bond locked?
+---
+
+Bond may be locked in the case of delayed penalties, typically for MEV stealing event reported by a dedicated committee. This measure ensures that node operators are held accountable for any misbehavior or rule violations.
diff --git a/faq/mainnet/locked-2.md b/faq/mainnet/locked-2.md
new file mode 100644
index 00000000..4aeaf545
--- /dev/null
+++ b/faq/mainnet/locked-2.md
@@ -0,0 +1,7 @@
+---
+title: How to unlock the bond?
+---
+
+To unlock the bond, the penalty amount, which includes the stolen amount and a fixed stealing fine, must be compensated on the "Locked bond" tab. This action can be performed from the Manager Address of the Node Operator.
+
+If there are disputes regarding the penalty, node operators can start a public discussion about the penalty applied on the Lido research forum under the [CSM Support](https://research.lido.fi/c/csm-support/21) category.
diff --git a/faq/mainnet/locked-3.md b/faq/mainnet/locked-3.md
new file mode 100644
index 00000000..d811e26f
--- /dev/null
+++ b/faq/mainnet/locked-3.md
@@ -0,0 +1,5 @@
+---
+title: Consequences of not compensating?
+---
+
+In case of refusal to compensate the penalty, a protocol rule violation occurs which results in the reset of all node operator benefits. The locked bond is burned to compensate all stETH holders for the rewards stolen.
diff --git a/faq/mainnet/main-1.md b/faq/mainnet/main-1.md
new file mode 100644
index 00000000..d543ade1
--- /dev/null
+++ b/faq/mainnet/main-1.md
@@ -0,0 +1,9 @@
+---
+title: Why run an Ethereum validator?
+---
+
+Running an Ethereum validator allows one to:
+
+1. **Receive Staking Rewards**: Validators get network rewards for performing their duties on the Ethereum blockchain (note: incorrectly or not performing duties incurs penalties).
+2. **Support the Network**: By running a validator, you actively contribute to the decentralization and security of the Ethereum network. Validators play a crucial role in reaching consensus and validating transactions, which enhances the network's reliability and resilience.
+3. **Learn and Engage with the community**: Operating a validator node provides valuable insights into blockchain technology and consensus mechanisms. Through hands-on experience, individuals can deepen their understanding of Ethereum's inner workings. Moreover, it provides an opportunity to engage with the Ethereum community, share knowledge, and contribute to the network's development.
diff --git a/faq/mainnet/main-2.md b/faq/mainnet/main-2.md
new file mode 100644
index 00000000..21c6b4a8
--- /dev/null
+++ b/faq/mainnet/main-2.md
@@ -0,0 +1,10 @@
+---
+title: What is required to be a Node Operator in CSM?
+---
+
+Node operation in CSM involves a long-term commitment to Ethereum's decentralization. Responsibilities include:
+
+- **Hardware Setup**: Setting up a computer that meets the system requirements for running validator nodes.
+- **Configuration**: Properly configuring nodes and validators, ensuring they are in sync with the Ethereum blockchain and other network participants.
+- **Security Measures**: Implementing robust security measures to safeguard against external threats and internal vulnerabilities.
+- **Maintenance**: Sustaining ongoing maintenance throughout the validators' lifespan, which involves monitoring performance, troubleshooting issues, and applying necessary updates.
diff --git a/faq/mainnet/main-3.md b/faq/mainnet/main-3.md
new file mode 100644
index 00000000..32d58fae
--- /dev/null
+++ b/faq/mainnet/main-3.md
@@ -0,0 +1,8 @@
+---
+title: What do node operators receive in CSM?
+---
+
+Node operators benefit from:
+
+- **Daily Bond Rebase**: The collateral for CSM NOs is eligible for [rewards through stETH's rebase](https://help.lido.fi/en/articles/5230610-what-is-steth), even before validator activation.
+- **Socialized Rewards**: Rewards are smoothed across the largest validator set, mitigating volatility. This means that even in the event of small outages or disruptions, node operators can still receive rewards, reducing the risk of rewards loss.
diff --git a/faq/mainnet/main-4.md b/faq/mainnet/main-4.md
new file mode 100644
index 00000000..72d4d148
--- /dev/null
+++ b/faq/mainnet/main-4.md
@@ -0,0 +1,12 @@
+---
+title: What are the risks of running a validator?
+---
+
+Node operators face several risks, including:
+
+1. **Technical Risk**: Maintaining reliable and secure hardware and software setups is essential. Any technical failure or vulnerability in the validator setup could lead to penalties.
+2. **Penalties for Misbehavior**: Validators can be penalized for various reasons, such as going offline or attempting to manipulate the network.
+3. **Market Risk**: The value of ETH can fluctuate significantly, impacting the value of the validators' staked ETH.
+4. **Network Risk**: Validators are part of a decentralized network, and the security and stability of the Ethereum network as a whole can affect individual validators. Events such as network attacks or protocol upgrades may impact validator operations, leading to potential disruptions or losses.
+5. **Operational Risk**: Validators require ongoing maintenance and monitoring to ensure smooth operation. Any operational issues, such as hardware failures or connectivity issues, could disrupt validator performance and result in rewards losses.
+6. **Slashing Risk**: Validators can be slashed, meaning they lose a portion of their staked ETH, for violating network rules or behaving maliciously. Slashing can occur due to actions such as double signing or failing to validate correctly, resulting in significant penalties.
diff --git a/faq/mainnet/main-5.md b/faq/mainnet/main-5.md
new file mode 100644
index 00000000..68b4053b
--- /dev/null
+++ b/faq/mainnet/main-5.md
@@ -0,0 +1,5 @@
+---
+title: How does CSM work?
+---
+
+Refer to [the CSM blog post](https://blog.lido.fi/lido-community-staking-an-overview/) for an overview, or [the CSM page](https://operatorportal.lido.fi/modules/community-staking-module) for a more detailed explanation of its mechanics and functionalities.
diff --git a/faq/mainnet/main-6.md b/faq/mainnet/main-6.md
new file mode 100644
index 00000000..e233beed
--- /dev/null
+++ b/faq/mainnet/main-6.md
@@ -0,0 +1,11 @@
+---
+title: What makes CSM unique?
+---
+
+CSM provides several unique features to attract and benefit community stakers:
+
+- **Rewards Smoothing**: Rewards, including Execution Layer (EL) rewards and MEV, are smoothed with other modules to provide more stable rewards comparable to operating validators solo.
+- **Low Bond Requirement**: A low bond for node operators makes participation more accessible to a broader range of prospective operators.
+- **Exclusive Use of ETH (stETH)**: CSM exclusively uses ETH ((w)stETH) for bond and rewards, eliminating the need for other assets and simplifying the process for node operators. The bond can be submitted as ETH, wstETH, or stETH, and both bond and rewards can be withdrawn in any of the three forms (withdrawals in the form of ETH follow the [normal Lido on Ethereum unstaking process](https://help.lido.fi/en/articles/7858323-how-do-i-unstake-my-steth)).
+- **Enhanced User Experience**: Accessible through a multitude of options -- from a web UI to integrations with Dappnode, Stereum, Eth-Docker, Sedge, Stereum, CoinPillar, etc., CSM offers a leading user-friendly experience, with reduced gas fees for on-chain operations and simplified transactions for joining and claiming rewards.
+- **Higher Reward Potential**: Node operators are potentially able to earn more rewards compared to vanilla solo staking, making CSM an attractive option for operators looking to run more validators to earn rewards.
diff --git a/faq/main-7.md b/faq/mainnet/main-7.md
similarity index 100%
rename from faq/main-7.md
rename to faq/mainnet/main-7.md
diff --git a/faq/main-7a.md b/faq/mainnet/main-7a.md
similarity index 100%
rename from faq/main-7a.md
rename to faq/mainnet/main-7a.md
diff --git a/faq/main-8.md b/faq/mainnet/main-8.md
similarity index 100%
rename from faq/main-8.md
rename to faq/mainnet/main-8.md
diff --git a/faq/mainnet/roles-1.md b/faq/mainnet/roles-1.md
new file mode 100644
index 00000000..430183f7
--- /dev/null
+++ b/faq/mainnet/roles-1.md
@@ -0,0 +1,23 @@
+---
+title: What are rewards and Manager Addresses?
+---
+
+There are two addresses associated with your Node Operator that have different scope of responsibilities for managing your Node Operator.
+
+The Rewards Address is used for:
+
+- Claiming bond and rewards
+- Adding extra bond amount
+- Proposing a new Rewards Address
+- Resetting the Manager Address to the current Rewards Address
+
+The Manager Address is used for:
+
+- Adding new keys
+- Removing existing keys
+- Adding extra bond amount
+- Claiming bond and rewards to the Rewards Address
+- Covering locked bond
+- Proposing a new Manager Address
+
+Read more about addresses management [here](https://operatorportal.lido.fi/modules/community-staking-module#block-d3ad2b2bd3994a06b19dccc0794ac8d6).
diff --git a/faq/mainnet/roles-2.md b/faq/mainnet/roles-2.md
new file mode 100644
index 00000000..348752d1
--- /dev/null
+++ b/faq/mainnet/roles-2.md
@@ -0,0 +1,7 @@
+---
+title: Why should these addresses be different?
+---
+
+It's recommended to use different addresses for security reasons. For example, a hot wallet may be used for the Manager Address to simplify daily operations, while a cold wallet (or something like a Safe) is preferable for the Rewards Address to enhance security.
+
+Read more about addresses management [here](https://operatorportal.lido.fi/modules/community-staking-module#block-d3ad2b2bd3994a06b19dccc0794ac8d6).
diff --git a/faq/mainnet/roles-3.md b/faq/mainnet/roles-3.md
new file mode 100644
index 00000000..893ec291
--- /dev/null
+++ b/faq/mainnet/roles-3.md
@@ -0,0 +1,5 @@
+---
+title: How to accept a change in address request?
+---
+
+To accept a change in address request, connect to the CSM widget with the new address, navigate to the "Roles" → "Inbox requests" tab, select and accept the request, and confirm the transaction in your wallet. Changes are made once the transaction is processed.
diff --git a/faq/mainnet/roles-4.md b/faq/mainnet/roles-4.md
new file mode 100644
index 00000000..1734995e
--- /dev/null
+++ b/faq/mainnet/roles-4.md
@@ -0,0 +1,5 @@
+---
+title: What to do if the change is submitted to a wrong address?
+---
+
+If a role change was submitted to the wrong address, the change can be revoked. For Rewards Address changes, navigate to "Roles" → "Rewards Address" → "Revoke". For Manager Address changes, go to "Roles" → "Manager Address" → "Revoke"
diff --git a/faq/mainnet/roles-5.md b/faq/mainnet/roles-5.md
new file mode 100644
index 00000000..90b335a5
--- /dev/null
+++ b/faq/mainnet/roles-5.md
@@ -0,0 +1,5 @@
+---
+title: What happens to rewards after changing the Rewards Address?
+---
+
+Rewards claimed to the previous Rewards Address remain there. After changing the Rewards Address, all rewards and excess bond accumulated on the bond balance can be claimed to the new Rewards Address. In the event of validator withdrawal, upon claiming of the bond, it would also be returned to the new Rewards Address.
diff --git a/faq/notifications-1.md b/faq/notifications-1.md
new file mode 100644
index 00000000..0b8df581
--- /dev/null
+++ b/faq/notifications-1.md
@@ -0,0 +1,9 @@
+---
+title: Why are notifications crucial?
+---
+
+Notifications are essential for staying informed about critical events within the Lido CSM protocol. By receiving alerts about exit requests, deposits, penalties, slashing incidents, and smart contract events, you can proactively manage your staking operations and address issues promptly.
+
+Staying informed helps reduce risks while maintaining transparency and control over your activities, ensuring smooth and efficient participation in the protocol.
+
+Learn more about this notifications in [our documentation](https://docs.dappnode.io/docs/user/staking/ethereum/lsd-pools/lido/notifications).
diff --git a/faq/notifications-2.md b/faq/notifications-2.md
new file mode 100644
index 00000000..d307dcbb
--- /dev/null
+++ b/faq/notifications-2.md
@@ -0,0 +1,7 @@
+---
+title: How to Get Your Telegram User ID
+---
+
+1. Open [Telegram](https://web.telegram.org/a/) and search for [`@userinfobot`](https://web.telegram.org/a/#52504489) or [`@raw_data_bot`](https://web.telegram.org/a/#1533228735).
+2. Start a chat with the bot by clicking Start.
+3. The bot will reply with your Telegram ID.
diff --git a/faq/notifications-3.md b/faq/notifications-3.md
new file mode 100644
index 00000000..db5fa1da
--- /dev/null
+++ b/faq/notifications-3.md
@@ -0,0 +1,10 @@
+---
+title: How to Create a Telegram Bot and Get the Bot Token
+---
+
+1. Open Telegram and search for [`@BotFather`](https://web.telegram.org/a/#93372553).
+2. Start a chat with BotFather and type `/newbot`.
+3. Follow the instructions to name your bot and choose a username (must end with "bot").
+4. Once created, BotFather will send you the bot token.
+ - Example: `123456789:ABCDefghIJKLMNOPQRSTuvwxYZ`.
+5. Open the chat with your bot and clib the "`Start`" button.
diff --git a/faq/performance-1.md b/faq/performance-1.md
new file mode 100644
index 00000000..5b60d56e
--- /dev/null
+++ b/faq/performance-1.md
@@ -0,0 +1,7 @@
+---
+title: What is the Lido threshold?
+---
+
+The Lido threshold is the value that determines whether a validator should receive rewards or not. It is calculated with the average of all the efficiencies (attestation rates) of all validators.
+
+Validators with an efficiency higher than the threshold will get rewards, while those below it won’t.
diff --git a/faq/performance-2.md b/faq/performance-2.md
new file mode 100644
index 00000000..794b1e49
--- /dev/null
+++ b/faq/performance-2.md
@@ -0,0 +1,5 @@
+---
+title: Where does the data come from?
+---
+
+We obtain the performance data of all Lido operators through its Smart Contract. The Lido CSM team distributes reports from all validators via IPFS hashes and Lido CSM package proccess it. Since this data is provided by Lido, is crucial in determining whether your validators qualify for rewards.
diff --git a/faq/performance-3.md b/faq/performance-3.md
new file mode 100644
index 00000000..1b5c7d79
--- /dev/null
+++ b/faq/performance-3.md
@@ -0,0 +1,5 @@
+---
+title: How often is the data updated?
+---
+
+The Lido CSM team distributes a new report every 28 days, so it can take up to almost a monthly delay when checking your current performance compared to other Lido operators.
diff --git a/faq/performance-4.md b/faq/performance-4.md
new file mode 100644
index 00000000..e3bcc0a6
--- /dev/null
+++ b/faq/performance-4.md
@@ -0,0 +1,5 @@
+---
+title: Where is this data stored?
+---
+
+The data collected from the Lido Smart Contract is stored in the Dappnode Lido package. Note that this data will include all samples from the validator; so the historical data from before the installation will be available.
diff --git a/faq/performance-5.md b/faq/performance-5.md
new file mode 100644
index 00000000..98eb5ad8
--- /dev/null
+++ b/faq/performance-5.md
@@ -0,0 +1,5 @@
+---
+title: Is the data accurate?
+---
+
+To calculate the efficiency, which is used to compare with the Lido threshold, we rely on data from the Lido Smart Contract. This source is considered 100% accurate since its the data that will be used by Lido when allocating the rewards.
diff --git a/faq/testnet-notifications-1.md b/faq/testnet-notifications-1.md
new file mode 100644
index 00000000..0b8df581
--- /dev/null
+++ b/faq/testnet-notifications-1.md
@@ -0,0 +1,9 @@
+---
+title: Why are notifications crucial?
+---
+
+Notifications are essential for staying informed about critical events within the Lido CSM protocol. By receiving alerts about exit requests, deposits, penalties, slashing incidents, and smart contract events, you can proactively manage your staking operations and address issues promptly.
+
+Staying informed helps reduce risks while maintaining transparency and control over your activities, ensuring smooth and efficient participation in the protocol.
+
+Learn more about this notifications in [our documentation](https://docs.dappnode.io/docs/user/staking/ethereum/lsd-pools/lido/notifications).
diff --git a/faq/testnet-notifications-2.md b/faq/testnet-notifications-2.md
new file mode 100644
index 00000000..d307dcbb
--- /dev/null
+++ b/faq/testnet-notifications-2.md
@@ -0,0 +1,7 @@
+---
+title: How to Get Your Telegram User ID
+---
+
+1. Open [Telegram](https://web.telegram.org/a/) and search for [`@userinfobot`](https://web.telegram.org/a/#52504489) or [`@raw_data_bot`](https://web.telegram.org/a/#1533228735).
+2. Start a chat with the bot by clicking Start.
+3. The bot will reply with your Telegram ID.
diff --git a/faq/testnet-notifications-3.md b/faq/testnet-notifications-3.md
new file mode 100644
index 00000000..db5fa1da
--- /dev/null
+++ b/faq/testnet-notifications-3.md
@@ -0,0 +1,10 @@
+---
+title: How to Create a Telegram Bot and Get the Bot Token
+---
+
+1. Open Telegram and search for [`@BotFather`](https://web.telegram.org/a/#93372553).
+2. Start a chat with BotFather and type `/newbot`.
+3. Follow the instructions to name your bot and choose a username (must end with "bot").
+4. Once created, BotFather will send you the bot token.
+ - Example: `123456789:ABCDefghIJKLMNOPQRSTuvwxYZ`.
+5. Open the chat with your bot and clib the "`Start`" button.
diff --git a/faq/testnet-performance-1.md b/faq/testnet-performance-1.md
new file mode 100644
index 00000000..5b60d56e
--- /dev/null
+++ b/faq/testnet-performance-1.md
@@ -0,0 +1,7 @@
+---
+title: What is the Lido threshold?
+---
+
+The Lido threshold is the value that determines whether a validator should receive rewards or not. It is calculated with the average of all the efficiencies (attestation rates) of all validators.
+
+Validators with an efficiency higher than the threshold will get rewards, while those below it won’t.
diff --git a/faq/testnet-performance-2.md b/faq/testnet-performance-2.md
new file mode 100644
index 00000000..794b1e49
--- /dev/null
+++ b/faq/testnet-performance-2.md
@@ -0,0 +1,5 @@
+---
+title: Where does the data come from?
+---
+
+We obtain the performance data of all Lido operators through its Smart Contract. The Lido CSM team distributes reports from all validators via IPFS hashes and Lido CSM package proccess it. Since this data is provided by Lido, is crucial in determining whether your validators qualify for rewards.
diff --git a/faq/testnet-performance-3.md b/faq/testnet-performance-3.md
new file mode 100644
index 00000000..75d4a96a
--- /dev/null
+++ b/faq/testnet-performance-3.md
@@ -0,0 +1,5 @@
+---
+title: How often is the data updated?
+---
+
+The Lido CSM team distributes a new report every 7 days, so it can take up to a weekly delay when checking your current performance compared to other Lido operators.
diff --git a/faq/testnet-performance-4.md b/faq/testnet-performance-4.md
new file mode 100644
index 00000000..e3bcc0a6
--- /dev/null
+++ b/faq/testnet-performance-4.md
@@ -0,0 +1,5 @@
+---
+title: Where is this data stored?
+---
+
+The data collected from the Lido Smart Contract is stored in the Dappnode Lido package. Note that this data will include all samples from the validator; so the historical data from before the installation will be available.
diff --git a/faq/testnet-performance-5.md b/faq/testnet-performance-5.md
new file mode 100644
index 00000000..98eb5ad8
--- /dev/null
+++ b/faq/testnet-performance-5.md
@@ -0,0 +1,5 @@
+---
+title: Is the data accurate?
+---
+
+To calculate the efficiency, which is used to compare with the Lido threshold, we rely on data from the Lido Smart Contract. This source is considered 100% accurate since its the data that will be used by Lido when allocating the rewards.
diff --git a/features/add-bond/add-bond-form/context/use-add-bond-form-network-data.tsx b/features/add-bond/add-bond-form/context/use-add-bond-form-network-data.tsx
index 3326d00e..54aedd5e 100644
--- a/features/add-bond/add-bond-form/context/use-add-bond-form-network-data.tsx
+++ b/features/add-bond/add-bond-form/context/use-add-bond-form-network-data.tsx
@@ -1,8 +1,4 @@
-import {
- useEthereumBalance,
- useSTETHBalance,
- useWSTETHBalance,
-} from '@lido-sdk/react';
+import { useEthereumBalance } from '@lido-sdk/react';
import { STRATEGY_LAZY } from 'consts/swr-strategies';
import { useNodeOperatorId } from 'providers/node-operator-provider';
import { useCallback, useMemo } from 'react';
@@ -10,6 +6,8 @@ import {
useCsmPaused,
useNodeOperatorBalance,
useStakingLimitInfo,
+ useSTETHBalance,
+ useWSTETHBalance,
} from 'shared/hooks';
import { type AddBondFormNetworkData } from '../context/types';
diff --git a/features/add-keys/add-keys/context/add-keys-form-provider.tsx b/features/add-keys/add-keys/context/add-keys-form-provider.tsx
index 33b13e32..d700d564 100644
--- a/features/add-keys/add-keys/context/add-keys-form-provider.tsx
+++ b/features/add-keys/add-keys/context/add-keys-form-provider.tsx
@@ -1,4 +1,4 @@
-import { FC, PropsWithChildren, useMemo } from 'react';
+import { FC, PropsWithChildren, useCallback, useMemo } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import {
FormControllerContext,
@@ -14,6 +14,8 @@ import { useAddKeysSubmit } from './use-add-keys-submit';
import { useAddKeysValidation } from './use-add-keys-validation';
import { useFormBondAmount } from './use-form-bond-amount';
import { useGetDefaultValues } from './use-get-default-values';
+// DAPPNODE
+import useBrainLaunchpadApi from 'dappnode/hooks/use-brain-launchpad-api';
export const useAddKeysFormData = useFormData;
@@ -23,6 +25,9 @@ export const AddKeysFormProvider: FC = ({ children }) => {
const asyncDefaultValues = useGetDefaultValues(networkData);
+ // DAPPNODE
+ const { submitKeystores: submitKeysToBrain } = useBrainLaunchpadApi();
+
const formObject = useForm({
defaultValues: asyncDefaultValues,
resolver: validationResolver,
@@ -39,15 +44,29 @@ export const AddKeysFormProvider: FC = ({ children }) => {
onRetry: retryFire,
});
+ // DAPPNODE
+ const handleSubmit = useCallback(
+ async (
+ input: AddKeysFormInputType,
+ networkData: AddKeysFormNetworkData,
+ ) => {
+ const { keystores, password } = formObject.getValues();
+ if (keystores && password)
+ await submitKeysToBrain({ keystores, password });
+ return await addKeys(input, networkData);
+ },
+ [addKeys, formObject, submitKeysToBrain], // dependencies
+ );
+
const formControllerValue: FormControllerContextValueType<
AddKeysFormInputType,
AddKeysFormNetworkData
> = useMemo(
() => ({
- onSubmit: addKeys,
+ onSubmit: handleSubmit,
retryEvent,
}),
- [addKeys, retryEvent],
+ [handleSubmit, retryEvent],
);
return (
diff --git a/features/add-keys/add-keys/context/types.ts b/features/add-keys/add-keys/context/types.ts
index d0feb472..c1cf006c 100644
--- a/features/add-keys/add-keys/context/types.ts
+++ b/features/add-keys/add-keys/context/types.ts
@@ -4,9 +4,17 @@ import { DepositDataInputType } from 'shared/hook-form/form-controller';
import { KeysAvailable, ShareLimitInfo } from 'shared/hooks';
import { BondBalance, LoadingRecord, NodeOperatorId } from 'types';
+// DAPPNODE
+export interface KeysFile {
+ name: string;
+ content: { pubkey: string };
+}
+
export type AddKeysFormInputType = {
token: TOKENS;
bondAmount?: BigNumber;
+ keystores?: KeysFile[]; //dappnode
+ password?: string; //dappnode
} & DepositDataInputType;
export type AddKeysFormNetworkData = {
diff --git a/features/add-keys/add-keys/context/use-add-keys-form-network-data.tsx b/features/add-keys/add-keys/context/use-add-keys-form-network-data.tsx
index 1bb8aeb2..6b8e2b79 100644
--- a/features/add-keys/add-keys/context/use-add-keys-form-network-data.tsx
+++ b/features/add-keys/add-keys/context/use-add-keys-form-network-data.tsx
@@ -1,8 +1,4 @@
-import {
- useEthereumBalance,
- useSTETHBalance,
- useWSTETHBalance,
-} from '@lido-sdk/react';
+import { useEthereumBalance } from '@lido-sdk/react';
import { STRATEGY_LAZY } from 'consts/swr-strategies';
import { useNodeOperatorId } from 'providers/node-operator-provider';
import { useCallback, useMemo } from 'react';
@@ -15,6 +11,8 @@ import {
useNodeOperatorCurveId,
useNonWithdrawnKeysCount,
useStakingLimitInfo,
+ useSTETHBalance,
+ useWSTETHBalance,
} from 'shared/hooks';
import { useBlockNumber } from 'wagmi';
import { type AddKeysFormNetworkData } from './types';
diff --git a/features/add-keys/add-keys/controls/keys-input.tsx b/features/add-keys/add-keys/controls/keys-input.tsx
index 91332778..a5e18a98 100644
--- a/features/add-keys/add-keys/controls/keys-input.tsx
+++ b/features/add-keys/add-keys/controls/keys-input.tsx
@@ -4,6 +4,10 @@ import { useFormState } from 'react-hook-form';
import { FormTitle, MatomoLink } from 'shared/components';
import { DepositDataInputHookForm } from 'shared/hook-form/controls';
import { AddKeysFormInputType } from '../context';
+// DAPPNODE
+import useCheckImportedDepositKeys from 'dappnode/hooks/use-check-deposit-keys';
+import { KeysBrainUpload } from 'dappnode/import-keys/keys-input-form';
+import { useFormContext } from 'react-hook-form';
export const KeysInput = () => {
const { errors } = useFormState({
@@ -11,6 +15,11 @@ export const KeysInput = () => {
});
const error = errors.rawDepositData?.message || errors.depositData?.message;
+ // DAPPNODE
+ const { watch } = useFormContext();
+ const depositDataValue = watch('depositData');
+ const { missingKeys } = useCheckImportedDepositKeys(depositDataValue);
+
return (
<>
{
Upload deposit data
+ {missingKeys.length > 0 && (
+
+ )}
>
);
};
diff --git a/features/create-node-operator/submit-keys-form/context/submit-keys-form-provider.tsx b/features/create-node-operator/submit-keys-form/context/submit-keys-form-provider.tsx
index e766ab02..db0827f8 100644
--- a/features/create-node-operator/submit-keys-form/context/submit-keys-form-provider.tsx
+++ b/features/create-node-operator/submit-keys-form/context/submit-keys-form-provider.tsx
@@ -1,5 +1,5 @@
import { useModifyContext } from 'providers/modify-provider';
-import { FC, PropsWithChildren, useMemo } from 'react';
+import { FC, PropsWithChildren, useCallback, useMemo } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import {
FormControllerContext,
@@ -18,6 +18,8 @@ import { useGetDefaultValues } from './use-get-default-values';
import { useSubmitKeysFormNetworkData } from './use-submit-keys-form-network-data';
import { useSubmitKeysSubmit } from './use-submit-keys-submit';
import { useSubmitKeysValidation } from './use-submit-keys-validation';
+// DAPPNODE
+import useBrainLaunchpadApi from 'dappnode/hooks/use-brain-launchpad-api';
export const useSubmitKeysFormData = useFormData;
@@ -39,21 +41,37 @@ export const SubmitKeysFormProvider: FC = ({ children }) => {
useFormDepositData(formObject);
const { retryEvent, retryFire } = useFormControllerRetry();
+ // DAPPNODE
+ const { submitKeystores: submitKeysToBrain } = useBrainLaunchpadApi();
const submitKeys = useSubmitKeysSubmit({
onConfirm: revalidate,
onRetry: retryFire,
});
+ // DAPPNODE
+ const handleSubmit = useCallback(
+ async (
+ input: SubmitKeysFormInputType,
+ networkData: SubmitKeysFormNetworkData,
+ ) => {
+ const { keystores, password } = formObject.getValues();
+ if (keystores && password)
+ await submitKeysToBrain({ keystores, password });
+ return await submitKeys(input, networkData);
+ },
+ [formObject, submitKeys, submitKeysToBrain], // dependencies
+ );
+
const formControllerValue: FormControllerContextValueType<
SubmitKeysFormInputType,
SubmitKeysFormNetworkData
> = useMemo(
() => ({
- onSubmit: submitKeys,
+ onSubmit: handleSubmit,
retryEvent,
}),
- [submitKeys, retryEvent],
+ [handleSubmit, retryEvent],
);
return (
diff --git a/features/create-node-operator/submit-keys-form/context/types.ts b/features/create-node-operator/submit-keys-form/context/types.ts
index 6391b1ae..8e081b30 100644
--- a/features/create-node-operator/submit-keys-form/context/types.ts
+++ b/features/create-node-operator/submit-keys-form/context/types.ts
@@ -1,5 +1,6 @@
import { type TOKENS } from 'consts/tokens';
import { BigNumber } from 'ethers';
+import { KeysFile } from 'features/add-keys/add-keys/context/types';
import { DepositDataInputType } from 'shared/hook-form/form-controller';
import { KeysAvailable, ShareLimitInfo } from 'shared/hooks';
import { LoadingRecord, Proof } from 'types';
@@ -14,6 +15,8 @@ export type SubmitKeysFormInputType = {
extendedManagerPermissions: boolean;
specifyCustomAddresses: boolean;
specifyReferrrer: boolean;
+ keystores?: KeysFile[]; // DAPPNODE
+ password?: string; // DAPPNODE
} & DepositDataInputType;
export type SubmitKeysFormNetworkData = {
diff --git a/features/create-node-operator/submit-keys-form/context/use-submit-keys-form-network-data.tsx b/features/create-node-operator/submit-keys-form/context/use-submit-keys-form-network-data.tsx
index 3aba60b8..16e70653 100644
--- a/features/create-node-operator/submit-keys-form/context/use-submit-keys-form-network-data.tsx
+++ b/features/create-node-operator/submit-keys-form/context/use-submit-keys-form-network-data.tsx
@@ -1,8 +1,4 @@
-import {
- useEthereumBalance,
- useSTETHBalance,
- useWSTETHBalance,
-} from '@lido-sdk/react';
+import { useEthereumBalance } from '@lido-sdk/react';
import { STRATEGY_LAZY } from 'consts/swr-strategies';
import { useCallback, useMemo } from 'react';
import {
@@ -14,6 +10,8 @@ import {
useKeysAvailable,
useKeysUploadLimit,
useStakingLimitInfo,
+ useSTETHBalance,
+ useWSTETHBalance,
} from 'shared/hooks';
import { useBlockNumber } from 'wagmi';
import { type SubmitKeysFormNetworkData } from './types';
diff --git a/features/create-node-operator/submit-keys-form/context/use-submit-keys-submit.ts b/features/create-node-operator/submit-keys-form/context/use-submit-keys-submit.ts
index 0a171f81..2fcc9779 100644
--- a/features/create-node-operator/submit-keys-form/context/use-submit-keys-submit.ts
+++ b/features/create-node-operator/submit-keys-form/context/use-submit-keys-submit.ts
@@ -22,10 +22,11 @@ import {
getAddedNodeOperator,
runWithTransactionLogger,
} from 'utils';
-import { Address } from 'wagmi';
+import { Address, useAccount } from 'wagmi';
import { useConfirmCustomAddressesModal } from '../hooks/use-confirm-modal';
import { useTxModalStagesSubmitKeys } from '../hooks/use-tx-modal-stages-submit-keys';
import { SubmitKeysFormInputType, SubmitKeysFormNetworkData } from './types';
+import useDappnodeUrls from 'dappnode/hooks/use-dappnode-urls';
type SubmitKeysOptions = {
onConfirm?: () => Promise | void;
@@ -128,6 +129,26 @@ export const useSubmitKeysSubmit = ({
const confirmCustomAddresses = useConfirmCustomAddressesModal();
const { ask } = useAskHowDidYouLearnCsm();
+ // DAPPNODE
+ const { backendUrl } = useDappnodeUrls();
+ const { address } = useAccount();
+ /**
+ * `scanEvents` is required to trigger a re-scan of events on the backend after a new node operator is added.
+ * By default, the backend does not re-scan events if the address is already indexed and the block difference
+ * since last scann is less than 320 blocks.
+ */
+ const scanEvents = useCallback(async () => {
+ const url = `${backendUrl}/api/v0/events_indexer/address_events?address=${address}&force=${true}`;
+ const options = {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ };
+ await fetch(url, options);
+ }, [backendUrl, address]);
+ // DAPPNODE
+
return useCallback(
async (
{
@@ -198,6 +219,14 @@ export const useSubmitKeysSubmit = ({
waitTx,
);
+ // DAPPNODE
+ await scanEvents().catch((e) => {
+ console.error(
+ `Failed to trigger forced events re-scan after creating new NO: ${e}`,
+ );
+ });
+ // DAPPNODE
+
const nodeOperator = getAddedNodeOperator(receipt);
txModalStages.success(
@@ -240,6 +269,7 @@ export const useSubmitKeysSubmit = ({
isUserOrZero,
onRetry,
ask,
+ scanEvents, // DAPPNODE
],
);
};
diff --git a/features/create-node-operator/submit-keys-form/controls/keys-input.tsx b/features/create-node-operator/submit-keys-form/controls/keys-input.tsx
index 95a53ef2..b3233016 100644
--- a/features/create-node-operator/submit-keys-form/controls/keys-input.tsx
+++ b/features/create-node-operator/submit-keys-form/controls/keys-input.tsx
@@ -4,6 +4,10 @@ import { useFormState } from 'react-hook-form';
import { FormTitle, MatomoLink } from 'shared/components';
import { DepositDataInputHookForm } from 'shared/hook-form/controls';
import { SubmitKeysFormInputType } from '../context';
+// DAPPNODE
+import useCheckImportedDepositKeys from 'dappnode/hooks/use-check-deposit-keys';
+import { KeysBrainUpload } from 'dappnode/import-keys/keys-input-form';
+import { useFormContext } from 'react-hook-form';
export const KeysInput = () => {
const { errors } = useFormState({
@@ -11,6 +15,11 @@ export const KeysInput = () => {
});
const error = errors.rawDepositData?.message || errors.depositData?.message;
+ // DAPPNODE
+ const { watch } = useFormContext();
+ const depositDataValue = watch('depositData');
+ const { missingKeys } = useCheckImportedDepositKeys(depositDataValue);
+
return (
<>
{
Upload deposit data
+ {missingKeys.length > 0 && (
+
+ )}
>
);
};
diff --git a/features/create-node-operator/submit-keys-form/hooks/use-tx-modal-stages-submit-keys.tsx b/features/create-node-operator/submit-keys-form/hooks/use-tx-modal-stages-submit-keys.tsx
index 2ea4e96c..56c363c7 100644
--- a/features/create-node-operator/submit-keys-form/hooks/use-tx-modal-stages-submit-keys.tsx
+++ b/features/create-node-operator/submit-keys-form/hooks/use-tx-modal-stages-submit-keys.tsx
@@ -14,6 +14,8 @@ import {
TxStageSuccess,
} from 'shared/transaction-modal/tx-stages-basic';
import { NodeOperatorId } from 'types';
+import { Button } from '@lidofinance/lido-ui';
+import { PATH } from 'consts/urls';
type Props = {
keysCount: number;
@@ -79,6 +81,16 @@ const getTxModalStagesSubmitKeys = (
+
+
>
) : undefined
}
diff --git a/features/dashboard/bond/available-to-claim.tsx b/features/dashboard/bond/available-to-claim.tsx
index f6beb05d..fd80c9c2 100644
--- a/features/dashboard/bond/available-to-claim.tsx
+++ b/features/dashboard/bond/available-to-claim.tsx
@@ -4,10 +4,9 @@ import { useNodeOperatorId } from 'providers/node-operator-provider';
import { FC } from 'react';
import { Counter, IconTooltip } from 'shared/components';
import {
- getNextRewardsFrame,
- useLastRewardsSlot,
useNodeOperatorBalance,
useNodeOperatorRewards,
+ useRewardsFrame,
} from 'shared/hooks';
import { useAvailableToClaim } from 'shared/hooks/useAvailableToClaim';
import { formatDate } from 'utils';
@@ -23,10 +22,8 @@ export const AvailableToClaim: FC = () => {
const { data: rewards, initialLoading: isRewardsLoading } =
useNodeOperatorRewards(id);
- const { data: rewardsSlot } = useLastRewardsSlot();
- const nextRewardsDate = rewardsSlot?.timestamp
- ? formatDate(getNextRewardsFrame(rewardsSlot.timestamp))
- : null;
+ const { data: rewardsFrame } = useRewardsFrame();
+ const nextRewardsDate = formatDate(rewardsFrame?.nextRewards);
const availableToClaim = useAvailableToClaim({
bond,
diff --git a/features/dashboard/bond/last-rewards.tsx b/features/dashboard/bond/last-rewards.tsx
index c73eb7e0..a7253537 100644
--- a/features/dashboard/bond/last-rewards.tsx
+++ b/features/dashboard/bond/last-rewards.tsx
@@ -6,7 +6,6 @@ import {
Text,
Tooltip,
} from '@lidofinance/lido-ui';
-import { differenceInCalendarDays, fromUnixTime } from 'date-fns';
import { ModalComponentType, useModal } from 'providers/modal-provider';
import { useNodeOperatorId } from 'providers/node-operator-provider';
import { FC, useCallback } from 'react';
@@ -19,14 +18,12 @@ import {
} from 'shared/components';
import { FaqElement } from 'shared/components/faq/styles';
import {
- getNextRewardsFrame,
- getPrevRewardsFrame,
useLastOperatorRewards,
- useLastRewardsSlot,
- useLastRewrdsTx,
useNodeOperatorInfo,
+ useRewardsFrame,
+ useSharesToSteth,
} from 'shared/hooks';
-import { formatDate, formatPercent } from 'utils';
+import { countDaysLeft, formatDate, formatPercent } from 'utils';
import { Balance } from './balance';
import {
AccordionStyle,
@@ -36,32 +33,23 @@ import {
RowHeader,
RowTitle,
} from './styles';
+import { useLastRewrdsTx } from 'dappnode/hooks/useLastRewardsFrame-api'; // DAPPNODE
export const LastRewards: FC = () => {
const { data: lastRewards, initialLoading: isLoading } =
useLastOperatorRewards();
+ const { data: distributed, initialLoading: isDistributedLoading } =
+ useSharesToSteth(lastRewards?.distributed);
const id = useNodeOperatorId();
const { data: info } = useNodeOperatorInfo(id);
- const { data: rewardsSlot } = useLastRewardsSlot();
+ const { data: rewardsFrame } = useRewardsFrame();
- const lastRewardsDate = rewardsSlot?.timestamp
- ? formatDate(rewardsSlot.timestamp)
- : null;
- const prevRewardsDate = rewardsSlot?.timestamp
- ? formatDate(getPrevRewardsFrame(rewardsSlot.timestamp))
- : null;
- const nextRewardsDate = rewardsSlot?.timestamp
- ? formatDate(getNextRewardsFrame(rewardsSlot.timestamp))
- : null;
-
- const daysLeft = rewardsSlot?.timestamp
- ? differenceInCalendarDays(
- fromUnixTime(getNextRewardsFrame(rewardsSlot.timestamp)),
- new Date(),
- )
- : null;
+ const lastRewardsDate = formatDate(rewardsFrame?.lastRewards);
+ const prevRewardsDate = formatDate(rewardsFrame?.prevRewards);
+ const nextRewardsDate = formatDate(rewardsFrame?.nextRewards);
+ const daysLeft = countDaysLeft(rewardsFrame?.nextRewards);
const showThisSection = lastRewards || (info?.totalDepositedKeys ?? 0) > 0;
@@ -83,8 +71,8 @@ export const LastRewards: FC = () => {
: undefined}
/>
@@ -98,7 +86,7 @@ export const LastRewards: FC = () => {
Next rewards distribution
- {rewardsSlot?.timestamp ? (
+ {lastRewardsDate && nextRewardsDate ? (
Report frame: {lastRewardsDate} — {nextRewardsDate}
diff --git a/features/dashboard/dashboard.tsx b/features/dashboard/dashboard.tsx
index 411a5d94..f85174bf 100644
--- a/features/dashboard/dashboard.tsx
+++ b/features/dashboard/dashboard.tsx
@@ -3,14 +3,26 @@ import { BondSection } from './bond';
import { KeysSection } from './keys';
import { RolesSection } from './roles';
import { ExternalSection } from './external';
+import { getConfig } from 'config';
+import { CHAINS } from 'consts/chains';
+import { SurveysCta } from './surveys-cta';
+import { StatusSection } from 'dappnode/status/status-section';
+import { NotificationsModal } from 'dappnode/notifications/notifications-modal';
+
+const { defaultChain } = getConfig();
export const Dashboard: FC = () => {
return (
<>
+ {/* DAPPNODE */}
+
+
+
+
-
+ {defaultChain !== CHAINS.Hoodi && }
>
);
};
diff --git a/features/dashboard/keys/keys-section.tsx b/features/dashboard/keys/keys-section.tsx
index de7e2680..c3eea72f 100644
--- a/features/dashboard/keys/keys-section.tsx
+++ b/features/dashboard/keys/keys-section.tsx
@@ -103,7 +103,7 @@ export const KeysSection: FC = () => {
tooltip="Keys that have already exited and withdrawn"
/>
- {keys?.length && (
+ {!!keys?.length && (
{
+ const required = useSurveysCall();
+ if (!required) return null;
+
+ return (
+
+ You're invited to voluntarily submit your validator setup data by
+ March 31st to help enhance the transparency of the Lido Protocol! Go to
+ the Surveys tab and fill out the
+ "Your Setup" form.
+
+ );
+};
diff --git a/features/starter-pack/paused-banner/paused-banner.tsx b/features/starter-pack/paused-banner/paused-banner.tsx
index ebfa1e5c..bb907fed 100644
--- a/features/starter-pack/paused-banner/paused-banner.tsx
+++ b/features/starter-pack/paused-banner/paused-banner.tsx
@@ -1,17 +1,8 @@
import { FC } from 'react';
import { BannerHeader, BlockStyled } from './styles';
-import { getConfig } from 'config';
-import { CHAINS } from 'consts/chains';
-import { HoleskyBanner } from 'features/welcome/holesky-banner';
-
-const { defaultChain } = getConfig();
export const PausedBanner: FC = () => {
- if (defaultChain === CHAINS.Holesky) {
- return ;
- }
-
return (
CSM is paused
diff --git a/features/starter-pack/stacter-pack-section/required-bond-amount.tsx b/features/starter-pack/stacter-pack-section/required-bond-amount.tsx
index 85b897e7..068eaea5 100644
--- a/features/starter-pack/stacter-pack-section/required-bond-amount.tsx
+++ b/features/starter-pack/stacter-pack-section/required-bond-amount.tsx
@@ -26,7 +26,7 @@ export const RequiredBondAmount: FC = () => {
) : (
)}
diff --git a/features/starter-pack/stacter-pack-section/starter-pack-section.tsx b/features/starter-pack/stacter-pack-section/starter-pack-section.tsx
index 16d6ffa1..ad8f8a5c 100644
--- a/features/starter-pack/stacter-pack-section/starter-pack-section.tsx
+++ b/features/starter-pack/stacter-pack-section/starter-pack-section.tsx
@@ -1,75 +1,30 @@
-import { MATOMO_CLICK_EVENTS_TYPES } from 'consts/matomo-click-events';
import { FC, PropsWithChildren } from 'react';
-import { MatomoLink } from 'shared/components';
-import { Partners } from './partners';
-import { Step } from './step';
-import { BlockStyled, Heading, Steps } from './styles';
-import { RequiredBondAmount } from './required-bond-amount';
-import {
- ABOUT_DEPOSIT_DATA_LINK,
- HOW_TO_GENERATE_DEPOSIT_DATA_LINK,
- PREPARE_HARDWARE_LINK,
-} from 'consts/external-links';
+import { BlockStyled, Heading } from './styles';
+import { Link } from '@lidofinance/lido-ui';
+import { dappnodeLidoDocsUrls } from 'dappnode/utils/dappnode-docs-urls';
+import { Steps } from 'dappnode/starter-pack/steps';
-export const StarterPackSection: FC = ({ children }) => (
-
-
-
CSM node operator starter pack
-
- Make sure you’ve completed all the basic steps before joining the{' '}
-
+export const StarterPackSection: FC = ({ children }) => {
+ return (
+
+
+
CSM in Dappnode starter pack
+
+ Make sure you've completed all the basic steps before joining the
Community Staking Module
-
-
-
-
-
- (stETH / wstETH equivalent) is required for the
- first validator
-
-
- Learn more
-
-
-
- Run{' '}
-
- your own hardware
- {' '}
- or use a cloud provider
-
-
- Do it{' '}
-
- manually
- {' '}
- or use Plug&Play solutions
-
-
-
- Prepare deposit data (.json file) for submitting keys
-
- Follow the{' '}
-
- generation guide
-
-
-
- {children}
-
-);
+
+
+ You can follow a guide in{' '}
+
+ {' '}
+ Dappnode's Documentation
+
+