diff --git a/.vscode/astral.code-workspace b/.vscode/astral.code-workspace index e728e1dea..458d52d60 100644 --- a/.vscode/astral.code-workspace +++ b/.vscode/astral.code-workspace @@ -43,6 +43,7 @@ "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": true, "editor.formatOnSave": true, "cSpell.words": [ + "autonomys", "extrinsics", "gemini", "graphiql", diff --git a/.vscode/settings.json b/.vscode/settings.json index 363aa2777..b3a0839e6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,6 +12,7 @@ "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": true, "editor.formatOnSave": true, "cSpell.words": [ + "autonomys", "extrinsics", "gemini", "graphiql", diff --git a/explorer/.env.sample b/explorer/.env.sample index 9f00e8536..0192466f0 100644 --- a/explorer/.env.sample +++ b/explorer/.env.sample @@ -4,6 +4,8 @@ NEXT_PUBLIC_PUBLIC_URL='/' NEXTAUTH_SECRET='secret-key-1234' NEXTAUTH_URL='http://localhost:3000/' +FAUNA_DB_SECRET="" + # Google Analytics ID GA_MEASUREMENT_ID=G- NEXT_PUBLIC_GOOGLE_ANALYTICS_ID='G-' diff --git a/explorer/next-auth.d.ts b/explorer/next-auth.d.ts index 8c39a5cbc..59a321891 100644 --- a/explorer/next-auth.d.ts +++ b/explorer/next-auth.d.ts @@ -1,5 +1,6 @@ +import type { Expr } from 'faunadb' import 'next-auth' -import { DiscordToken, SubspaceToken } from 'types/jwt' +import type { DiscordToken, SubspaceToken } from 'types/jwt' // Extending the 'next-auth' module to include custom user and session types declare module 'next-auth' { @@ -16,6 +17,11 @@ declare module 'next-auth' { user: User | null token: string } + + export interface SavedUser extends User { + createdAt: Expr + updatedAt: Expr + } } // Extending the 'next-auth/client' module for client-side usage diff --git a/explorer/package.json b/explorer/package.json index 5177dab50..1d9bceeb1 100644 --- a/explorer/package.json +++ b/explorer/package.json @@ -44,6 +44,7 @@ "@vercel/og": "^0.6.2", "dayjs": "^1.11.10", "ethers": "^6.11.1", + "faunadb": "^4.8.0", "formik": "^2.4.5", "graphql": "^16.6.0", "js-cookie": "^3.0.5", diff --git a/explorer/src/app/[chain]/consensus/accounts/[accountId]/image/route.tsx b/explorer/src/app/[chain]/consensus/accounts/[accountId]/image/route.tsx index 718494425..6504cdd14 100644 --- a/explorer/src/app/[chain]/consensus/accounts/[accountId]/image/route.tsx +++ b/explorer/src/app/[chain]/consensus/accounts/[accountId]/image/route.tsx @@ -64,9 +64,6 @@ function Screen({ accountById: AccountByIdQuery['accountById'] }) { dayjs.extend(relativeTime) - console.log('chainMatch', chainMatch) - console.log('accountId', accountId) - console.log('accountById', accountById) const account = { total: accountById?.total ?? '0', @@ -128,7 +125,7 @@ function Screen({ }} tw='absolute text-xl text-white p-4 ml-30 font-bold' > - Total {numberWithCommas(bigNumberToNumber(account.total))} tSSC + Total {numberWithCommas(bigNumberToNumber(account.total))} ({chainMatch.token.symbol}) - Reserved {numberWithCommas(bigNumberToNumber(account.reserved))} tSSC + Reserved {numberWithCommas(bigNumberToNumber(account.reserved))} ( + {chainMatch.token.symbol}) - Free {numberWithCommas(bigNumberToNumber(account.free))} tSSC + Free {numberWithCommas(bigNumberToNumber(account.free))} ({chainMatch.token.symbol}) diff --git a/explorer/src/app/[chain]/nova/accounts/[accountId]/image/route.tsx b/explorer/src/app/[chain]/nova/accounts/[accountId]/image/route.tsx index 718494425..5519f3214 100644 --- a/explorer/src/app/[chain]/nova/accounts/[accountId]/image/route.tsx +++ b/explorer/src/app/[chain]/nova/accounts/[accountId]/image/route.tsx @@ -128,7 +128,7 @@ function Screen({ }} tw='absolute text-xl text-white p-4 ml-30 font-bold' > - Total {numberWithCommas(bigNumberToNumber(account.total))} tSSC + Total {numberWithCommas(bigNumberToNumber(account.total))} ({chainMatch.token.symbol}) - Reserved {numberWithCommas(bigNumberToNumber(account.reserved))} tSSC + Reserved {numberWithCommas(bigNumberToNumber(account.reserved))} ( + {chainMatch.token.symbol}) - Free {numberWithCommas(bigNumberToNumber(account.free))} tSSC + Free {numberWithCommas(bigNumberToNumber(account.free))} ({chainMatch.token.symbol}) diff --git a/explorer/src/app/[chain]/operators/[operatorId]/page.tsx b/explorer/src/app/[chain]/staking/[operatorId]/page.tsx similarity index 100% rename from explorer/src/app/[chain]/operators/[operatorId]/page.tsx rename to explorer/src/app/[chain]/staking/[operatorId]/page.tsx diff --git a/explorer/src/app/[chain]/operators/layout.tsx b/explorer/src/app/[chain]/staking/layout.tsx similarity index 55% rename from explorer/src/app/[chain]/operators/layout.tsx rename to explorer/src/app/[chain]/staking/layout.tsx index 629512efc..3ce106d5b 100644 --- a/explorer/src/app/[chain]/operators/layout.tsx +++ b/explorer/src/app/[chain]/staking/layout.tsx @@ -1,7 +1,7 @@ import { MainLayout } from 'components/layout/Layout' -import { OperatorHeader } from 'components/layout/OperatorHeader' +import { StakingHeader } from 'components/layout/StakingHeader' import type { ChildrenPageProps } from 'types/app' export default async function Layout({ children }: ChildrenPageProps) { - return }>{children} + return }>{children} } diff --git a/explorer/src/app/[chain]/operators/manage/page.tsx b/explorer/src/app/[chain]/staking/manage/page.tsx similarity index 100% rename from explorer/src/app/[chain]/operators/manage/page.tsx rename to explorer/src/app/[chain]/staking/manage/page.tsx diff --git a/explorer/src/app/[chain]/operators/nomination/page.tsx b/explorer/src/app/[chain]/staking/nomination/page.tsx similarity index 100% rename from explorer/src/app/[chain]/operators/nomination/page.tsx rename to explorer/src/app/[chain]/staking/nomination/page.tsx diff --git a/explorer/src/app/[chain]/operators/nominators/page.tsx b/explorer/src/app/[chain]/staking/nominators/page.tsx similarity index 100% rename from explorer/src/app/[chain]/operators/nominators/page.tsx rename to explorer/src/app/[chain]/staking/nominators/page.tsx diff --git a/explorer/src/app/[chain]/operators/page.tsx b/explorer/src/app/[chain]/staking/page.tsx similarity index 100% rename from explorer/src/app/[chain]/operators/page.tsx rename to explorer/src/app/[chain]/staking/page.tsx diff --git a/explorer/src/app/[chain]/operators/stake/page.tsx b/explorer/src/app/[chain]/staking/register/page.tsx similarity index 91% rename from explorer/src/app/[chain]/operators/stake/page.tsx rename to explorer/src/app/[chain]/staking/register/page.tsx index 351650cf2..f51153a9b 100644 --- a/explorer/src/app/[chain]/operators/stake/page.tsx +++ b/explorer/src/app/[chain]/staking/register/page.tsx @@ -6,7 +6,7 @@ import type { ChainPageProps } from 'types/app' export async function generateMetadata({ params: { chain } }: ChainPageProps): Promise { const chainTitle = chains.find((c) => c.urls.page === chain)?.title || 'Unknown chain' - const title = `${metadata.title} - ${chainTitle} - Stake My Operator` + const title = `${metadata.title} - ${chainTitle} - Register Operator` return { ...metadata, title, diff --git a/explorer/src/components/Account/AccountBalanceStats.tsx b/explorer/src/components/Account/AccountBalanceStats.tsx index b02ea5ae4..c72de3989 100644 --- a/explorer/src/components/Account/AccountBalanceStats.tsx +++ b/explorer/src/components/Account/AccountBalanceStats.tsx @@ -1,5 +1,6 @@ import { StatItem } from 'components/common/StatItem' import { Account } from 'gql/graphql' +import useDomains from 'hooks/useDomains' import { FC } from 'react' import { bigNumberToNumber, numberWithCommas } from 'utils/number' import { AccountBalancePieChart } from './AccountBalancePieChart' @@ -10,6 +11,7 @@ type Props = { } export const AccountBalanceStats: FC = ({ account, isDesktop = false }) => { + const { selectedChain } = useDomains() const accountTotal = bigNumberToNumber(account.total || 0) const accountFree = bigNumberToNumber(account.free || 0) const accountReserved = bigNumberToNumber(account.reserved || 0) @@ -28,7 +30,9 @@ export const AccountBalanceStats: FC = ({ account, isDesktop = false }) =
{numberWithCommas(accountTotal)}
-
tSSC
+
+ {selectedChain.token.symbol} +
@@ -36,17 +40,17 @@ export const AccountBalanceStats: FC = ({ account, isDesktop = false }) =
-
+
-
+
diff --git a/explorer/src/components/Account/AccountExtrinsicList.tsx b/explorer/src/components/Account/AccountExtrinsicList.tsx index ae6261c4a..35d499c5f 100644 --- a/explorer/src/components/Account/AccountExtrinsicList.tsx +++ b/explorer/src/components/Account/AccountExtrinsicList.tsx @@ -17,7 +17,6 @@ import { useErrorHandler } from 'react-error-boundary' import { downloadFullData } from 'utils/downloadFullData' import { sort } from 'utils/sort' import { shortString } from 'utils/string' -import { ExtrinsicListCard } from '../Extrinsic/ExtrinsicListCard' import { AccountExtrinsicFilterDropdown } from './AccountExtrinsicFilterDropdown' import { QUERY_ACCOUNT_EXTRINSICS } from './query' @@ -176,9 +175,9 @@ export const AccountExtrinsicList: FC = ({ accountId }) => { return (
-
+
-
Action Filter:
+
Action Filter:
@@ -194,24 +193,8 @@ export const AccountExtrinsicList: FC = ({ accountId }) => { onPaginationChange={setPagination} filename='account-extrinsic-list' fullDataDownloader={fullDataDownloader} - mobileComponent={} />
) } - -type MobileComponentProps = { - extrinsics: Extrinsic[] -} - -const MobileComponent: FC = ({ extrinsics }) => ( -
- {extrinsics.map((extrinsic, index) => ( - - ))} -
-) diff --git a/explorer/src/components/Account/AccountGraphTabs.tsx b/explorer/src/components/Account/AccountGraphTabs.tsx index cc93e0b02..a1cbc3e6c 100644 --- a/explorer/src/components/Account/AccountGraphTabs.tsx +++ b/explorer/src/components/Account/AccountGraphTabs.tsx @@ -1,4 +1,5 @@ import { TabTitle } from 'components/common/Tabs' +import useDomains from 'hooks/useDomains' import React, { FC, ReactElement, useState } from 'react' import { bigNumberToNumber, numberWithCommas } from 'utils/number' @@ -10,6 +11,7 @@ type Props = { export const AccountGraphTabs: FC = ({ children, total, isDesktop = false }) => { const [selectedTab, setSelectedTab] = useState(0) + const { selectedChain } = useDomains() const tabStyle = isDesktop ? 'bg-white border border-slate-100 shadow rounded-[20px] p-4 dark:bg-gradient-to-r dark:from-gradientTwilight dark:via-gradientDusk dark:to-gradientSunset dark:border-none' @@ -29,7 +31,9 @@ export const AccountGraphTabs: FC = ({ children, total, isDesktop = false
{total ? numberWithCommas(bigNumberToNumber(total)) : 0}
-
tSSC
+
+ {selectedChain.token.symbol} +
diff --git a/explorer/src/components/Account/AccountLatestRewards.tsx b/explorer/src/components/Account/AccountLatestRewards.tsx index 221b530a5..88d1bbac2 100644 --- a/explorer/src/components/Account/AccountLatestRewards.tsx +++ b/explorer/src/components/Account/AccountLatestRewards.tsx @@ -18,27 +18,25 @@ interface AccountLatestRewardsProps { export const AccountLatestRewards: FC = ({ rewards }) => { const { selectedChain, selectedDomain } = useDomains() - const { accountId } = useParams() - const { push } = useRouter() return ( -
+
-
+
Block Number
-
+
Type
-
+
Amount
-
    +
      {rewards.map(({ id, name, block, amount }, index) => (
    1. = ({ rewards }) .join(' ')}
- {bigNumberToNumber(amount)} tSSC + {bigNumberToNumber(amount)} {selectedChain.token.symbol}
))} @@ -93,7 +91,7 @@ export const AccountLatestRewards: FC = ({ rewards }) ), ) } - className='mt-4 w-full rounded-[20px] bg-blueLight py-4 dark:bg-whiteTransparent dark:text-white' + className='bg-blueLight dark:bg-whiteTransparent mt-4 w-full rounded-[20px] py-4 dark:text-white' > See All Rewards diff --git a/explorer/src/components/Account/AccountPreviousRewards.tsx b/explorer/src/components/Account/AccountPreviousRewards.tsx index 5345b24bd..1d68fdc56 100644 --- a/explorer/src/components/Account/AccountPreviousRewards.tsx +++ b/explorer/src/components/Account/AccountPreviousRewards.tsx @@ -218,21 +218,21 @@ export const AccountPreviousRewards: FC = () => { }, [handleSearch]) return ( -
+
-
+
Testnet
-
- Localized tSSC +
+ Localized {selectedChain.token.symbol}
-
+
Mainnet allocation %
-
    +
      {rewardsPhase.map((phase, index) => (
    1. = ({ accountId, total }) => { const { isDark } = useTheme() - + const { selectedChain } = useDomains() const lastWeek = dayjs().subtract(3, 'month').utc().format() const { data, error, loading } = useQuery(QUERY_LAST_WEEK_REWARDS, { @@ -74,7 +75,9 @@ export const AccountRewardGraph: FC = ({ accountId, total }) => {
      {total ? numberWithCommas(bigNumberToNumber(total)) : 0}
      -
      tSSC
      +
      + {selectedChain.token.symbol} +
{parsedData.length > 0 ? ( diff --git a/explorer/src/components/Account/AccountRewardTable.tsx b/explorer/src/components/Account/AccountRewardTable.tsx index 70d197c27..3f38686ec 100644 --- a/explorer/src/components/Account/AccountRewardTable.tsx +++ b/explorer/src/components/Account/AccountRewardTable.tsx @@ -11,7 +11,6 @@ import useDomains from 'hooks/useDomains' import useMediaQuery from 'hooks/useMediaQuery' import Link from 'next/link' import { FC, useMemo } from 'react' -import { AccountRewardListCard } from './AccountRewardListCard' dayjs.extend(relativeTime) @@ -102,7 +101,8 @@ export const AccountRewardTable: FC = ({ enableSorting: true, cell: ({ row }) => (
- {row.original.amount ? bigNumberToNumber(row.original.amount) : 0} tSSC + {row.original.amount ? bigNumberToNumber(row.original.amount) : 0}{' '} + {selectedChain.token.symbol}
), }, @@ -122,17 +122,6 @@ export const AccountRewardTable: FC = ({ pagination={pagination} pageCount={pageCount} onPaginationChange={setPagination} - mobileComponent={ -
- {rewards.map((reward, index) => ( - - ))} -
- } />
diff --git a/explorer/src/components/Leaderboard/NominatorRewardsList.tsx b/explorer/src/components/Leaderboard/NominatorRewardsList.tsx index e1d5ee83f..32312420b 100644 --- a/explorer/src/components/Leaderboard/NominatorRewardsList.tsx +++ b/explorer/src/components/Leaderboard/NominatorRewardsList.tsx @@ -16,12 +16,11 @@ import { AccountRewards, AccountsNominatorsConnectionRewardsQuery } from 'gql/gr import useDomains from 'hooks/useDomains' import useMediaQuery from 'hooks/useMediaQuery' import Link from 'next/link' -import { FC, useCallback, useMemo, useState } from 'react' +import { useCallback, useMemo, useState } from 'react' import { useErrorHandler } from 'react-error-boundary' import { downloadFullData } from 'utils/downloadFullData' import { sort } from 'utils/sort' import { NotFound } from '../layout/NotFound' -import { NominatorRewardsListCard } from './NominatorRewardsListCard' import { QUERY_NOMINATORS_REWARDS_LIST } from './querys' type Row = { @@ -83,7 +82,9 @@ export const NominatorRewardsList = () => { enableSorting: true, cell: ({ row }) => (
- {row.original.operator ? `${bigNumberToString(row.original.operator, 10)} tSSC` : 0} + {row.original.operator + ? `${bigNumberToString(row.original.operator, 10)} ${selectedChain.token.symbol}` + : 0}
), }, @@ -171,13 +172,13 @@ export const NominatorRewardsList = () => {
-
+
Nominators Leaderboard
{ onPaginationChange={setPagination} filename='leaderboard-nominator-rewards-list' fullDataDownloader={fullDataDownloader} - mobileComponent={} />
) } - -type MobileComponentProps = { - accountRewards: AccountRewards[] -} - -const MobileComponent: FC = ({ accountRewards }) => ( -
- {accountRewards.map((account, index) => ( - - ))} -
-) diff --git a/explorer/src/components/Leaderboard/NominatorRewardsListCard.tsx b/explorer/src/components/Leaderboard/NominatorRewardsListCard.tsx index 70b000531..2fcca9e67 100644 --- a/explorer/src/components/Leaderboard/NominatorRewardsListCard.tsx +++ b/explorer/src/components/Leaderboard/NominatorRewardsListCard.tsx @@ -18,7 +18,9 @@ export const NominatorRewardsListCard: FC = ({ account, index }) => { { name: 'Rank', value: index }, { name: 'Nominator reward', - value: account.operator ? `${numberWithCommas(bigNumberToNumber(account.operator))} tSSC` : 0, + value: account.operator + ? `${numberWithCommas(bigNumberToNumber(account.operator))} ${selectedChain.token.symbol}` + : 0, }, ] return ( @@ -34,7 +36,7 @@ export const NominatorRewardsListCard: FC = ({ account, index }) => { account.id, )} > -

+

{account.id}

diff --git a/explorer/src/components/Leaderboard/OperatorRewardsList.tsx b/explorer/src/components/Leaderboard/OperatorRewardsList.tsx index fa585cb7c..0cafa33d1 100644 --- a/explorer/src/components/Leaderboard/OperatorRewardsList.tsx +++ b/explorer/src/components/Leaderboard/OperatorRewardsList.tsx @@ -15,13 +15,12 @@ import type { OperatorsConnectionRewardsQuery } from 'gql/graphql' import useDomains from 'hooks/useDomains' import useMediaQuery from 'hooks/useMediaQuery' import Link from 'next/link' -import { FC, useCallback, useMemo, useState } from 'react' +import { useCallback, useMemo, useState } from 'react' import { useErrorHandler } from 'react-error-boundary' import type { Cell } from 'types/table' import { downloadFullData } from 'utils/downloadFullData' import { sort } from 'utils/sort' import { NotFound } from '../layout/NotFound' -import { OperatorRewardsListCard } from './OperatorRewardsListCard' import { QUERY_OPERATORS_REWARDS_LIST } from './querys' export const OperatorRewardsList = () => { @@ -89,7 +88,7 @@ export const OperatorRewardsList = () => { >) => (
{row.original.amount - ? `${numberWithCommas(bigNumberToNumber(row.original.amount))} tSSC` + ? `${numberWithCommas(bigNumberToNumber(row.original.amount))} ${selectedChain.token.symbol}` : 0}
), @@ -176,13 +175,13 @@ export const OperatorRewardsList = () => {
-
+
Operators Leaderboard
{ onPaginationChange={setPagination} filename='leaderboard-operator-rewards-list' fullDataDownloader={fullDataDownloader} - mobileComponent={} />
) } -export default OperatorRewardsList - -type MobileComponentProps = { - operatorRewards: OperatorsConnectionRewardsQuery['operatorRewardsConnection']['edges'][0]['node'][] -} -const MobileComponent: FC = ({ operatorRewards }) => ( -
- {operatorRewards.map((operator, index) => ( - - ))} -
-) +export default OperatorRewardsList diff --git a/explorer/src/components/Leaderboard/OperatorRewardsListCard.tsx b/explorer/src/components/Leaderboard/OperatorRewardsListCard.tsx index 8a5ae68ef..2edc9c1da 100644 --- a/explorer/src/components/Leaderboard/OperatorRewardsListCard.tsx +++ b/explorer/src/components/Leaderboard/OperatorRewardsListCard.tsx @@ -17,7 +17,9 @@ export const OperatorRewardsListCard: FC = ({ operator, index }) => { { name: 'Rank', value: index }, { name: 'Operator reward', - value: operator.amount ? `${numberWithCommas(bigNumberToNumber(operator.amount))} tSSC` : 0, + value: operator.amount + ? `${numberWithCommas(bigNumberToNumber(operator.amount))} ${selectedChain.token.symbol}` + : 0, }, ] return ( @@ -32,7 +34,7 @@ export const OperatorRewardsListCard: FC = ({ operator, index }) => { operator.id, )} > -

+

{operator.id}

diff --git a/explorer/src/components/Leaderboard/VoteBlockRewardList.tsx b/explorer/src/components/Leaderboard/VoteBlockRewardList.tsx index 75da56571..fca2dc385 100644 --- a/explorer/src/components/Leaderboard/VoteBlockRewardList.tsx +++ b/explorer/src/components/Leaderboard/VoteBlockRewardList.tsx @@ -16,13 +16,12 @@ import type { AccountsConnectionRewardsQuery } from 'gql/graphql' import useDomains from 'hooks/useDomains' import useMediaQuery from 'hooks/useMediaQuery' import Link from 'next/link' -import { FC, useCallback, useMemo, useState } from 'react' +import { useCallback, useMemo, useState } from 'react' import { useErrorHandler } from 'react-error-boundary' import type { Cell } from 'types/table' import { downloadFullData } from 'utils/downloadFullData' import { sort } from 'utils/sort' import { NotFound } from '../layout/NotFound' -import { VoteBlockRewardListCard } from './VoteBlockRewardListCard' import { QUERY_REWARDS_LIST } from './querys' export const VoteBlockRewardList = () => { @@ -90,7 +89,7 @@ export const VoteBlockRewardList = () => { >) => (
{row.original.block - ? `${numberWithCommas(bigNumberToNumber(row.original.block))} tSSC` + ? `${numberWithCommas(bigNumberToNumber(row.original.block))} ${selectedChain.token.symbol}` : 0}
), @@ -106,7 +105,7 @@ export const VoteBlockRewardList = () => { >) => (
{row.original.vote - ? `${numberWithCommas(bigNumberToNumber(row.original.vote))} tSSC` + ? `${numberWithCommas(bigNumberToNumber(row.original.vote))} ${selectedChain.token.symbol}` : 0}
), @@ -122,7 +121,7 @@ export const VoteBlockRewardList = () => { >) => (
{row.original.amount - ? `${numberWithCommas(bigNumberToNumber(row.original.amount))} tSSC` + ? `${numberWithCommas(bigNumberToNumber(row.original.amount))} ${selectedChain.token.symbol}` : 0}
), @@ -206,13 +205,13 @@ export const VoteBlockRewardList = () => {
-
+
Farmers Leaderboard
{ onPaginationChange={setPagination} filename='leaderboard-vote-block-reward-list' fullDataDownloader={fullDataDownloader} - mobileComponent={} />
) } - -type MobileComponentProps = { - accounts: AccountsConnectionRewardsQuery['accountRewardsConnection']['edges'][0]['node'][] -} - -export const MobileComponent: FC = ({ accounts }) => ( -
- {accounts.map((account, index) => ( - - ))} -
-) diff --git a/explorer/src/components/Leaderboard/VoteBlockRewardListCard.tsx b/explorer/src/components/Leaderboard/VoteBlockRewardListCard.tsx index 5199fb807..d359a9097 100644 --- a/explorer/src/components/Leaderboard/VoteBlockRewardListCard.tsx +++ b/explorer/src/components/Leaderboard/VoteBlockRewardListCard.tsx @@ -18,15 +18,21 @@ export const VoteBlockRewardListCard: FC = ({ account, index }) => { { name: 'Rank', value: index }, { name: 'Block reward', - value: account.block ? `${numberWithCommas(bigNumberToNumber(account.block))} tSSC` : 0, + value: account.block + ? `${numberWithCommas(bigNumberToNumber(account.block))} ${selectedChain.token.symbol}` + : 0, }, { name: 'Vote reward', - value: account.vote ? `${numberWithCommas(bigNumberToNumber(account.vote))} tSSC` : 0, + value: account.vote + ? `${numberWithCommas(bigNumberToNumber(account.vote))} ${selectedChain.token.symbol}` + : 0, }, { name: 'Total reward', - value: account.amount ? `${numberWithCommas(bigNumberToNumber(account.amount))} tSSC` : 0, + value: account.amount + ? `${numberWithCommas(bigNumberToNumber(account.amount))} ${selectedChain.token.symbol}` + : 0, }, ] return ( @@ -42,7 +48,7 @@ export const VoteBlockRewardListCard: FC = ({ account, index }) => { account.id, )} > -

+

{account.id}

diff --git a/explorer/src/components/Operator/ActionsModal.tsx b/explorer/src/components/Operator/ActionsModal.tsx index 513a99a8e..e39706f65 100644 --- a/explorer/src/components/Operator/ActionsModal.tsx +++ b/explorer/src/components/Operator/ActionsModal.tsx @@ -1,9 +1,11 @@ 'use client' +import { WalletType } from '@/constants' import { floatToStringWithDecimals, formatUnitsToNumber } from '@/utils/number' import { sendGAEvent } from '@next/third-parties/google' import { Modal } from 'components/common/Modal' import { Field, FieldArray, Form, Formik, FormikState } from 'formik' +import useDomains from 'hooks/useDomains' import useWallet from 'hooks/useWallet' import Slider from 'rc-slider' import 'rc-slider/assets/index.css' @@ -12,7 +14,7 @@ import * as Yup from 'yup' export enum OperatorActionType { None = 'none', - AddFunds = 'Add Funds', + Nominating = 'Nominate', Withdraw = 'Withdraw', Deregister = 'Deregister', UnlockFunds = 'Unlock Funds', @@ -43,7 +45,8 @@ interface FormValues { const AMOUNT_TO_SUBTRACT_FROM_MAX_AMOUNT = 0.0001 export const ActionsModal: FC = ({ isOpen, action, onClose }) => { - const { api, actingAccount, injector } = useWallet() + const { selectedChain } = useDomains() + const { api, actingAccount, subspaceAccount, injector } = useWallet() const [formError, setFormError] = useState(null) const [tokenDecimals, setTokenDecimals] = useState(0) const [tokenSymbol, setTokenSymbol] = useState('') @@ -95,10 +98,12 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) => { const loadWalletBalance = useCallback(async () => { if (!api || !actingAccount) return - const balance = await api.query.system.account(actingAccount.address) - setWalletBalance( - formatUnitsToNumber((balance.toJSON() as { data: { free: string } }).data.free), - ) + if (actingAccount.type === WalletType.subspace) { + const balance = await api.query.system.account(actingAccount.address) + setWalletBalance( + formatUnitsToNumber((balance.toJSON() as { data: { free: string } }).data.free), + ) + } }, [api, actingAccount]) const handleClose = useCallback(() => { @@ -113,6 +118,7 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) => { ) => { if (!api || !actingAccount || !injector) return setFormError('We are not able to connect to the blockchain') + if (!subspaceAccount) return setFormError('Not a subspace account connected') if (action.operatorId === null) return setFormError('Please select an operator to add funds to') @@ -138,7 +144,7 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) => { sendGAEvent('event', 'error', { value: 'nominateOperator' }) } }, - [api, actingAccount, injector, action.operatorId, tokenDecimals, handleClose], + [api, actingAccount, injector, subspaceAccount, action.operatorId, tokenDecimals, handleClose], ) const handleWithdraw = useCallback( @@ -148,6 +154,7 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) => { ) => { if (!api || !actingAccount || !injector) return setFormError('We are not able to connect to the blockchain') + if (!subspaceAccount) return setFormError('Not a subspace account connected') if (action.operatorId === null) return setFormError('Please select an operator to add funds to') @@ -170,12 +177,13 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) => { sendGAEvent('event', 'error', { value: 'withdrawStake' }) } }, - [api, actingAccount, injector, action.operatorId, handleClose], + [api, actingAccount, injector, subspaceAccount, action.operatorId, handleClose], ) const handleDeregister = useCallback(async () => { if (!api || !actingAccount || !injector) return setFormError('We are not able to connect to the blockchain') + if (!subspaceAccount) return setFormError('Not a subspace account connected') if (action.operatorId === null) return setFormError('Please select an operator to add funds to') try { @@ -195,11 +203,12 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) => { console.error('Error', error) sendGAEvent('event', 'error', { value: 'deregisterOperator' }) } - }, [actingAccount, action.operatorId, api, injector, handleClose]) + }, [api, actingAccount, injector, subspaceAccount, action.operatorId, handleClose]) const handleUnlockFunds = useCallback(async () => { if (!api || !actingAccount || !injector) return setFormError('We are not able to connect to the blockchain') + if (!subspaceAccount) return setFormError('Not a subspace account connected') if (action.operatorId === null) return setFormError('Please select an operator to add funds to') try { @@ -219,11 +228,12 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) => { console.error('Error', error) sendGAEvent('event', 'error', { value: 'unlockFunds' }) } - }, [actingAccount, action.operatorId, api, injector, handleClose]) + }, [api, actingAccount, injector, subspaceAccount, action.operatorId, handleClose]) const handleUnlockOperator = useCallback(async () => { if (!api || !actingAccount || !injector) return setFormError('We are not able to connect to the blockchain') + if (!subspaceAccount) return setFormError('Not a subspace account connected') if (action.operatorId === null) return setFormError('Please select an operator to add funds to') try { @@ -243,7 +253,7 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) => { console.error('Error', error) sendGAEvent('event', 'error', { value: 'unlockOperator' }) } - }, [actingAccount, action.operatorId, api, injector, handleClose]) + }, [api, actingAccount, injector, subspaceAccount, action.operatorId, handleClose]) const ErrorPlaceholder = useMemo( () => @@ -259,7 +269,7 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) => { const ActionBody = useMemo(() => { switch (OperatorActionType[action.type as keyof typeof OperatorActionType]) { - case OperatorActionType.AddFunds: + case OperatorActionType.Nominating: return (
= ({ isOpen, action, onClose }) => { validationSchema={addFundsFormValidationSchema} onSubmit={(values, { resetForm }) => OperatorActionType[action.type as keyof typeof OperatorActionType] === - OperatorActionType.AddFunds + OperatorActionType.Nominating ? handleAddFunds(values, resetForm) : handleWithdraw(values, resetForm) } @@ -281,10 +291,11 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) => { {`Amount to ${ OperatorActionType[action.type as keyof typeof OperatorActionType] === - OperatorActionType.AddFunds + OperatorActionType.Nominating ? 'stake' : 'withdraw' - }`} + }`}{' '} + ({selectedChain.token.symbol}) = ({ isOpen, action, onClose }) => { type='number' placeholder={`Amount to ${ OperatorActionType[action.type as keyof typeof OperatorActionType] === - OperatorActionType.AddFunds + OperatorActionType.Nominating ? 'stake' : 'withdraw' }`} @@ -354,7 +365,7 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) => { validationSchema={withdrawFundsFormValidationSchema} onSubmit={(values, { resetForm }) => OperatorActionType[action.type as keyof typeof OperatorActionType] === - OperatorActionType.AddFunds + OperatorActionType.Nominating ? handleAddFunds(values, resetForm) : handleWithdraw(values, resetForm) } @@ -368,7 +379,7 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) => { {`Amount to ${ OperatorActionType[action.type as keyof typeof OperatorActionType] === - OperatorActionType.AddFunds + OperatorActionType.Nominating ? 'stake' : 'withdraw' }`} @@ -496,10 +507,11 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) => { handleUnlockOperator, handleAddFunds, handleWithdraw, + selectedChain.token.symbol, maxAmountToAdd, actingAccount, - maxSharesToWithdraw, sliderValue, + maxSharesToWithdraw, ]) useEffect(() => { diff --git a/explorer/src/components/Operator/NominationManagement.tsx b/explorer/src/components/Operator/NominationManagement.tsx index deb309348..66007aa06 100644 --- a/explorer/src/components/Operator/NominationManagement.tsx +++ b/explorer/src/components/Operator/NominationManagement.tsx @@ -24,7 +24,6 @@ import { sort } from 'utils/sort' import { capitalizeFirstLetter } from 'utils/string' import { ActionsDropdown, ActionsDropdownRow } from './ActionsDropdown' import { ActionsModal, OperatorAction, OperatorActionType } from './ActionsModal' -import { NominatorListCard } from './NominatorListCard' import { QUERY_NOMINATOR_CONNECTION_LIST } from './query' export const NominationManagement: FC = () => { @@ -134,7 +133,7 @@ export const NominationManagement: FC = () => { ), ), )}{' '} - tSSC + {selectedChain.token.symbol}
), }, @@ -167,7 +166,7 @@ export const NominationManagement: FC = () => { cell: ({ row, }: Cell) => ( -
{`${bigNumberToNumber(row.original.operator.minimumNominatorStake)} tSSC`}
+
{`${bigNumberToNumber(row.original.operator.minimumNominatorStake)} ${selectedChain.token.symbol}`}
), }, { @@ -187,7 +186,7 @@ export const NominationManagement: FC = () => { cell: ({ row, }: Cell) => ( -
{`${bigNumberToNumber(row.original.operator.currentTotalStake)} tSSC`}
+
{`${bigNumberToNumber(row.original.operator.currentTotalStake)} ${selectedChain.token.symbol}`}
), }, { @@ -230,7 +229,7 @@ export const NominationManagement: FC = () => { }, ] return cols - }, [selectedChain.urls.page, selectedDomain, action, handleAction]) + }, [selectedChain.urls.page, selectedChain.token.symbol, selectedDomain, action, handleAction]) const orderBy = useMemo(() => sort(sorting, 'id_ASC'), [sorting]) @@ -335,7 +334,7 @@ export const NominationManagement: FC = () => { > Information across nominations {subspaceAccount && ( -
+
-
+
@@ -416,7 +408,7 @@ export const NominationManagement: FC = () => { isDesktop ? 'text-base' : 'text-sm' } font-medium dark:text-white`} > - {bigNumberToNumber(totalInStake)} tSSC + {bigNumberToNumber(totalInStake)} {selectedChain.token.symbol}
@@ -427,26 +419,3 @@ export const NominationManagement: FC = () => {
) } - -export default NominationManagement - -type MobileComponentProps = { - nominators: NominatorsConnectionQuery['nominatorsConnection']['edges'][0]['node'][] - action: OperatorAction - handleAction: (value: OperatorAction) => void -} - -const MobileComponent: FC = ({ nominators, action, handleAction }) => ( -
- {nominators.map((nominator, index) => ( - - ))} -
-) diff --git a/explorer/src/components/Operator/NominatorListCard.tsx b/explorer/src/components/Operator/NominatorListCard.tsx deleted file mode 100644 index e9c596c04..000000000 --- a/explorer/src/components/Operator/NominatorListCard.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { numberWithCommas } from '@/utils/number' -import { shortString } from '@/utils/string' -import Identicon from '@polkadot/react-identicon' -import { MobileCard } from 'components/common/MobileCard' -import { INTERNAL_ROUTES } from 'constants/routes' -import { NominatorsConnectionQuery } from 'gql/oldSquidTypes' -import useDomains from 'hooks/useDomains' -import Link from 'next/link' -import { FC } from 'react' -import { ActionsDropdown } from './ActionsDropdown' -import { OperatorAction, OperatorActionType } from './ActionsModal' - -type Props = { - nominator: NominatorsConnectionQuery['nominatorsConnection']['edges'][0]['node'] - action?: OperatorAction - handleAction?: (value: OperatorAction) => void - index?: number - excludeActions?: OperatorActionType[] - nominatorMaxShares?: bigint -} - -export const NominatorListCard: FC = ({ - nominator, - action, - handleAction, - excludeActions, - nominatorMaxShares, -}) => { - const { selectedChain, selectedDomain } = useDomains() - - const body = [ - { name: 'Id', value: nominator.account.id ? shortString(nominator.account.id) : 'Unknown' }, - { name: 'Shares', value: nominator.shares ? numberWithCommas(nominator.shares) : 0 }, - ] - return ( - - - -

- {nominator.account.id} -

- - {action && handleAction && ( - - )} -
- } - body={body} - /> - ) -} diff --git a/explorer/src/components/Operator/NominatorsList.tsx b/explorer/src/components/Operator/NominatorsList.tsx index 4abf14c04..b7d7d66a1 100644 --- a/explorer/src/components/Operator/NominatorsList.tsx +++ b/explorer/src/components/Operator/NominatorsList.tsx @@ -25,7 +25,6 @@ import { sort } from 'utils/sort' import { capitalizeFirstLetter } from 'utils/string' import { ActionsDropdown, ActionsDropdownRow } from './ActionsDropdown' import { ActionsModal, OperatorAction, OperatorActionType } from './ActionsModal' -import { NominatorListCard } from './NominatorListCard' import { QUERY_NOMINATOR_CONNECTION_LIST } from './query' export const NominatorsList: FC = () => { @@ -135,7 +134,7 @@ export const NominatorsList: FC = () => { ), ), )}{' '} - tSSC + ${selectedChain.token.symbol}
), }, @@ -168,7 +167,7 @@ export const NominatorsList: FC = () => { cell: ({ row, }: Cell) => ( -
{`${bigNumberToNumber(row.original.operator.minimumNominatorStake)} tSSC`}
+
{`${bigNumberToNumber(row.original.operator.minimumNominatorStake)} ${selectedChain.token.symbol}`}
), }, { @@ -188,7 +187,7 @@ export const NominatorsList: FC = () => { cell: ({ row, }: Cell) => ( -
{`${bigNumberToNumber(row.original.operator.currentTotalStake)} tSSC`}
+
{`${bigNumberToNumber(row.original.operator.currentTotalStake)} ${selectedChain.token.symbol}`}
), }, { @@ -233,7 +232,14 @@ export const NominatorsList: FC = () => { }, }) return cols - }, [subspaceAccount, selectedChain.urls.page, selectedDomain, action, handleAction]) + }, [ + subspaceAccount, + selectedChain.urls.page, + selectedChain.token.symbol, + selectedDomain, + action, + handleAction, + ]) const orderBy = useMemo(() => sort(sorting, 'id_ASC'), [sorting]) @@ -300,7 +306,7 @@ export const NominatorsList: FC = () => {
-
{`Nominators (${totalLabel})`}
+
{`Nominators (${totalLabel})`}
@@ -318,14 +324,6 @@ export const NominatorsList: FC = () => { pageSizeOptions={[10]} filename='operators-nominators-list' fullDataDownloader={fullDataDownloader} - mobileComponent={ - - } />
@@ -333,33 +331,3 @@ export const NominatorsList: FC = () => {
) } - -export default NominatorsList - -type MobileComponentProps = { - nominators: NominatorsConnectionQuery['nominatorsConnection']['edges'][0]['node'][] - action: OperatorAction - handleAction: (value: OperatorAction) => void - subspaceAccount?: string -} - -const MobileComponent: FC = ({ nominators, action, handleAction }) => ( -
- {nominators.map((nominator, index) => ( - - ))} -
-) diff --git a/explorer/src/components/Operator/Operator.tsx b/explorer/src/components/Operator/Operator.tsx index 29011af79..f404106f9 100644 --- a/explorer/src/components/Operator/Operator.tsx +++ b/explorer/src/components/Operator/Operator.tsx @@ -8,7 +8,7 @@ import { useParams } from 'next/navigation' import { FC } from 'react' import { useErrorHandler } from 'react-error-boundary' import { OperatorDetailsCard } from './OperatorDetailsCard' -import { OperatorNominatorList } from './OperatorNominatorList' +import { OperatorNominatorTable } from './OperatorNominatorTable' import { QUERY_OPERATOR_BY_ID } from './query' export const Operator: FC = () => { @@ -34,7 +34,11 @@ export const Operator: FC = () => { return (
- +
+
+ +
+
) } diff --git a/explorer/src/components/Operator/OperatorDetailsCard.tsx b/explorer/src/components/Operator/OperatorDetailsCard.tsx index 00272bdf3..020fcfe00 100644 --- a/explorer/src/components/Operator/OperatorDetailsCard.tsx +++ b/explorer/src/components/Operator/OperatorDetailsCard.tsx @@ -5,6 +5,7 @@ import { List, StyledListItem } from 'components/common/List' import dayjs from 'dayjs' import relativeTime from 'dayjs/plugin/relativeTime' import { Operator } from 'gql/graphql' +import useDomains from 'hooks/useDomains' import { FC } from 'react' dayjs.extend(relativeTime) @@ -15,9 +16,11 @@ type Props = { } export const OperatorDetailsCard: FC = ({ operator, isDesktop = false }) => { + const { selectedChain } = useDomains() + return (
-
+

Operator #{operator.id} @@ -37,11 +40,11 @@ export const OperatorDetailsCard: FC = ({ operator, isDesktop = false }) - {bigNumberToNumber(operator.minimumNominatorStake)} tSSC + {bigNumberToNumber(operator.minimumNominatorStake)} ${selectedChain.token.symbol} {operator.nominationTax} % - {bigNumberToNumber(operator.currentTotalStake)} tSSC + {bigNumberToNumber(operator.currentTotalStake)} ${selectedChain.token.symbol} {numberWithCommas(operator.totalShares)} {operator.status} diff --git a/explorer/src/components/Operator/OperatorManagement.tsx b/explorer/src/components/Operator/OperatorManagement.tsx index 95f0fab1b..b110dc749 100644 --- a/explorer/src/components/Operator/OperatorManagement.tsx +++ b/explorer/src/components/Operator/OperatorManagement.tsx @@ -24,7 +24,6 @@ import { sort } from 'utils/sort' import { capitalizeFirstLetter } from 'utils/string' import { ActionsDropdown, ActionsDropdownRow } from './ActionsDropdown' import { ActionsModal, OperatorAction, OperatorActionType } from './ActionsModal' -import { OperatorsListCard } from './OperatorsListCard' import { QUERY_OPERATOR_CONNECTION_LIST } from './query' export const OperatorManagement: FC = () => { @@ -214,7 +213,7 @@ export const OperatorManagement: FC = () => { cell: ({ row, }: Cell) => ( -
{`${bigNumberToNumber(row.original.minimumNominatorStake)} tSSC`}
+
{`${bigNumberToNumber(row.original.minimumNominatorStake)} ${selectedChain.token.symbol}`}
), }, { @@ -234,7 +233,7 @@ export const OperatorManagement: FC = () => { cell: ({ row, }: Cell) => ( -
{`${bigNumberToNumber(row.original.currentTotalStake)} tSSC`}
+
{`${bigNumberToNumber(row.original.currentTotalStake)} ${selectedChain.token.symbol}`}
), }, { @@ -281,7 +280,14 @@ export const OperatorManagement: FC = () => { ), }, ] - }, [selectedChain.urls.page, selectedDomain, action, handleAction, lastBlock]) + }, [ + selectedChain.urls.page, + selectedChain.token.symbol, + selectedDomain, + lastBlock, + action, + handleAction, + ]) useEffect(() => { if (subspaceAccount) handleSearch(subspaceAccount) @@ -313,7 +319,7 @@ export const OperatorManagement: FC = () => { on Account {subspaceAccount} @@ -335,19 +341,11 @@ export const OperatorManagement: FC = () => { fullDataDownloader={fullDataDownloader} pageSizeOptions={[10]} filename='operators-operator-management-list' - mobileComponent={ - - } />

-
+
@@ -403,7 +401,7 @@ export const OperatorManagement: FC = () => { isDesktop ? 'text-base' : 'text-sm' } font-medium dark:text-white`} > - {bigNumberToNumber(totalInStake)} tSSC + {bigNumberToNumber(totalInStake)} {selectedChain.token.symbol}
@@ -421,7 +419,7 @@ export const OperatorManagement: FC = () => { isDesktop ? 'text-base' : 'text-sm' } font-medium dark:text-white`} > - {bigNumberToNumber(totalOperatorStake)} tSSC* + {bigNumberToNumber(totalOperatorStake)} {selectedChain.token.symbol}*
@@ -440,7 +438,7 @@ export const OperatorManagement: FC = () => { isDesktop ? 'text-base' : 'text-sm' } font-medium dark:text-white`} > - {bigNumberToNumber(totalOperatorStake)} tSSC + {bigNumberToNumber(totalOperatorStake)} {selectedChain.token.symbol}
@@ -458,7 +456,7 @@ export const OperatorManagement: FC = () => { isDesktop ? 'text-base' : 'text-sm' } font-medium dark:text-white`} > - {bigNumberToNumber(totalNominatorsStake)} tSSC + {bigNumberToNumber(totalNominatorsStake)} {selectedChain.token.symbol}
@@ -469,35 +467,3 @@ export const OperatorManagement: FC = () => {
) } - -type MobileComponentProps = { - operators: OperatorsConnectionQuery['operatorsConnection']['edges'][0]['node'][] - action: OperatorAction - handleAction: (value: OperatorAction) => void - lastBlock?: number -} - -const MobileComponent: FC = ({ - operators, - action, - handleAction, - lastBlock, -}) => ( -
- {operators.map((operator, index) => ( - - ))} -
-) diff --git a/explorer/src/components/Operator/OperatorNominatorList.tsx b/explorer/src/components/Operator/OperatorNominatorList.tsx deleted file mode 100644 index 79ebd9765..000000000 --- a/explorer/src/components/Operator/OperatorNominatorList.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { Operator } from 'gql/graphql' -import { FC } from 'react' -import { OperatorNominatorTable } from './OperatorNominatorTable' - -type Props = { - operator: Operator - isDesktop: boolean -} - -export const OperatorNominatorList: FC = ({ operator, isDesktop }) => { - return ( -
-
- -
-
- ) -} diff --git a/explorer/src/components/Operator/OperatorNominatorTable.tsx b/explorer/src/components/Operator/OperatorNominatorTable.tsx index aad459cbf..26d2409ee 100644 --- a/explorer/src/components/Operator/OperatorNominatorTable.tsx +++ b/explorer/src/components/Operator/OperatorNominatorTable.tsx @@ -1,86 +1,91 @@ import { numberWithCommas } from '@/utils/number' import { shortString } from '@/utils/string' import Identicon from '@polkadot/react-identicon' -import { Column, Table } from 'components/common/Table' +import { NewTable } from 'components/common/NewTable' import { INTERNAL_ROUTES } from 'constants/routes' import { Nominator, Operator } from 'gql/graphql' import useDomains from 'hooks/useDomains' import useMediaQuery from 'hooks/useMediaQuery' import Link from 'next/link' -import { FC } from 'react' -import { NominatorListCard } from './NominatorListCard' +import { FC, useMemo } from 'react' +import type { Cell } from 'types/table' interface Props { operator: Operator - isDesktop?: boolean } -export const OperatorNominatorTable: FC = ({ operator, isDesktop }) => { +export const OperatorNominatorTable: FC = ({ operator }) => { const { selectedChain, selectedDomain } = useDomains() const isLargeLaptop = useMediaQuery('(min-width: 1440px)') - // methods - const generateColumns = (nominators: Nominator[]): Column[] => [ - { - title: 'Account Id', - cells: nominators.map(({ account, id }) => ( -
- - -
{isLargeLaptop ? account.id : shortString(account.id)}
- -
- )), - }, - { - title: 'Shares', - cells: nominators.map(({ shares, id }, index) => { - return ( -
{shares ? numberWithCommas(shares) : 0}
- ) - }), - }, - { - title: 'Owner', - cells: nominators.map(({ id, account }, index) => { - const isOwner = operator.operatorOwner === account.id - - return
{isOwner ? 'Yes' : 'No'}
- }), - }, - ] - - // constants - const columns = generateColumns(operator.nominators) + const columns = useMemo( + () => [ + { + accessorKey: 'account', + header: 'Account Id', + cell: ({ row }: Cell) => ( +
+ + +
+ {isLargeLaptop ? row.original.account.id : shortString(row.original.account.id)} +
+ +
+ ), + }, + { + accessorKey: 'shares', + header: 'Shares', + cell: ({ row }: Cell) => { + const percent = (row.original.shares / operator.totalShares) * 100 + const isOwner = operator.operatorOwner === row.original.account.id + return ( + <> + {isLargeLaptop && ( +
{row.original.shares ? numberWithCommas(row.original.shares) : 0}
+ )} + {percent.toFixed(2)}%{isOwner ? 'Yes' : 'No'} + + ) + }, + }, + { + accessorKey: 'owner', + header: 'is Owner', + cell: ({ row }: Cell) => + operator.operatorOwner === row.original.account.id ? 'Yes' : 'No', + }, + ], + [ + isLargeLaptop, + operator.operatorOwner, + operator.totalShares, + selectedChain.urls.page, + selectedDomain, + ], + ) - return isDesktop ? ( -
-
- - - - ) : ( -
- {operator.nominators.map((nominator, index) => ( - - ))} -
+ return ( + ) } diff --git a/explorer/src/components/Operator/OperatorStake.tsx b/explorer/src/components/Operator/OperatorStake.tsx index ac59d43e9..8bb9fd38e 100644 --- a/explorer/src/components/Operator/OperatorStake.tsx +++ b/explorer/src/components/Operator/OperatorStake.tsx @@ -1,26 +1,34 @@ 'use client' import { WalletIcon } from '@/components/icons' -import { floatToStringWithDecimals } from '@/utils/number' +import { shortString } from '@/utils/string' import { Listbox, Transition } from '@headlessui/react' -import { CheckIcon, ChevronDownIcon } from '@heroicons/react/20/solid' +import { CheckIcon, ChevronDownIcon, ExclamationTriangleIcon } from '@heroicons/react/20/solid' import { sendGAEvent } from '@next/third-parties/google' -import { isHex } from '@polkadot/util' +import { Keyring } from '@polkadot/api' +import { createType } from '@polkadot/types' +import { isHex, u8aToHex } from '@polkadot/util' import { PreferredExtensionModal } from 'components/layout/PreferredExtensionModal' import { EXTERNAL_ROUTES } from 'constants/routes' -import { Field, Form, Formik, FormikState } from 'formik' +import { Field, Form, Formik, FormikErrors, FormikState } from 'formik' +import useDomains from 'hooks/useDomains' import useMediaQuery from 'hooks/useMediaQuery' import useWallet from 'hooks/useWallet' import Link from 'next/link' import React, { Fragment, useCallback, useEffect, useMemo, useState } from 'react' +import { floatToStringWithDecimals } from 'utils/number' import * as Yup from 'yup' +import { ConnectWalletButton } from '../common/ConnectWalletButton' interface FormValues { domainId: number signingKey: string + signature: string | Uint8Array amountToStake: number nominatorTax: number minimumNominatorStake: number + signingKeySeed: string + signingKeystore: Blob | null } type OperatorAllowListRestricted = { operators: string[] } @@ -32,8 +40,14 @@ type Domain = { operatorAllowList: OperatorAllowList } +enum OwnershipProofMethod { + seed = 'seed', + keystore = 'keystore', +} + export const OperatorStake = () => { const [isOpen, setIsOpen] = useState(false) + const { selectedChain } = useDomains() const { api, actingAccount, subspaceAccount, injector } = useWallet() const [formError, setFormError] = useState(null) const isDesktop = useMediaQuery('(min-width: 640px)') @@ -42,13 +56,19 @@ export const OperatorStake = () => { const [minOperatorStake, setMinOperatorStake] = useState(0) const [tokenDecimals, setTokenDecimals] = useState(0) const [tokenSymbol, setTokenSymbol] = useState('') + const [activeProofMethodTab, setActiveProofMethodTab] = useState( + OwnershipProofMethod.keystore, + ) const initialValues: FormValues = { domainId: 0, signingKey: '', + signature: '0x', amountToStake: 0, nominatorTax: 0, minimumNominatorStake: 0, + signingKeySeed: '', + signingKeystore: null, } const loadDomains = useCallback(async () => { @@ -131,8 +151,9 @@ export const OperatorStake = () => { values: FormValues, resetForm: (nextState?: Partial> | undefined) => void, ) => { - if (!api || !actingAccount || !injector) + if (!api || !subspaceAccount || !actingAccount || !injector) return setFormError('We are not able to connect to the blockchain') + if (!subspaceAccount) throw new Error('No subspace account') try { const block = await api.rpc.chain.getBlock() @@ -148,6 +169,7 @@ export const OperatorStake = () => { ), nominationTax: values.nominatorTax.toString(), }, + values.signature, ) .signAndSend(actingAccount.address, { signer: injector.signer }) @@ -161,7 +183,7 @@ export const OperatorStake = () => { } resetForm() }, - [actingAccount, api, injector, tokenDecimals], + [actingAccount, api, injector, subspaceAccount, tokenDecimals], ) const handleConnectWallet = useCallback((e: React.MouseEvent) => { @@ -169,6 +191,100 @@ export const OperatorStake = () => { setIsOpen(true) }, []) + const resetActiveProofMethodTab = useCallback( + ( + method: OwnershipProofMethod, + values: FormValues, + resetForm: (nextState?: Partial> | undefined) => void, + ) => { + setActiveProofMethodTab(method) + resetForm({ + values: { + ...values, + signingKey: initialValues.signingKey, + signingKeySeed: initialValues.signingKeySeed, + signature: initialValues.signature, + }, + }) + }, + [initialValues.signature, initialValues.signingKey, initialValues.signingKeySeed], + ) + + const handleProof = useCallback( + ( + seed: string, + setFieldValue: ( + field: string, + value: string | Uint8Array, + shouldValidate?: boolean | undefined, + ) => Promise>, + ) => { + if (!api || !subspaceAccount || !actingAccount) + return setFormError('We are not able to connect to the blockchain') + + try { + const OperatorKeyring = new Keyring({ type: 'sr25519' }) + const Operator = OperatorKeyring.addFromUri(seed) + + const signingKey = u8aToHex(Operator.publicKey) + const signature = Operator.sign( + createType(api.registry, 'AccountId', actingAccount.address).toU8a(), + ) + setFieldValue('signingKey', signingKey) + setFieldValue('signature', signature) + } catch (error) { + setFormError('There was an error with the seed') + console.error('Error', error) + } + }, + [subspaceAccount, actingAccount, api], + ) + + const handleProofOfOwnershipWithSeed = useCallback( + ( + values: FormValues, + setFieldValue: ( + field: string, + value: string | Uint8Array, + shouldValidate?: boolean | undefined, + ) => Promise>, + ) => handleProof(values.signingKeySeed, setFieldValue), + [handleProof], + ) + + const handleProofOfOwnershipWithKeystore = useCallback( + ( + events: React.ChangeEvent, + setFieldValue: ( + field: string, + value: string | Uint8Array, + shouldValidate?: boolean | undefined, + ) => Promise>, + ) => { + if (!events.target.files) return setFormError('No file') + try { + const fileReader = new FileReader() + fileReader.onload = () => { + const keystoreContent = fileReader.result as string + if (fileReader.readyState === 2) { + try { + const seed = keystoreContent.replace(/"|_/g, '') + handleProof(seed, setFieldValue) + } catch (error) { + setFormError('There was an error with the keystore') + console.error('Error', error) + } + } + } + fileReader.readAsText(events.target.files[0]) + } catch (error) { + setFormError('There was an error with the keystore') + console.error('Error', error) + } + }, + [handleProof], + ) + useEffect(() => { loadDomains() }, [api, loadDomains]) @@ -189,9 +305,9 @@ export const OperatorStake = () => {
- tSSC holders (Gemini 3h testnet network only) can stake their tSSC to add more - security to the protocol and earn Staking Incentives. Learn more about the risks - involved. + {tokenSymbol} holders (Gemini 3h testnet network only) can stake their {tokenSymbol}{' '} + to add more security to the protocol and earn Staking Incentives. Learn more about the + risks involved.
Step 1: Setup a node @@ -207,9 +323,46 @@ export const OperatorStake = () => { Please follow the docs to setup a node
+
+ Step 2: Connect your wallet +
+ +
+ {!actingAccount ? ( +
+ +
+ ) : ( +
+ {subspaceAccount ? ( + <> + {isDesktop ? subspaceAccount : shortString(subspaceAccount)}{' '} + + + ) : ( + <> + {isDesktop ? actingAccount.address : shortString(actingAccount.address)}{' '} + +
+ + )} +
+ )} +
+ {actingAccount && + (actingAccount as unknown as { type: string }).type === 'ethereum' && ( +
+ EVM account not supported for this action +
+ )} +
- Step 2: Register + Step 3: Register
{ +
- Signing key + Proof of Ownership - +
+ + +
+
+ + {activeProofMethodTab === OwnershipProofMethod.seed && ( + <> +
+ + Signing key seed + + - {errors.signingKey && touched.signingKey ? ( -
- {errors.signingKey} + /> + {errors.signingKeySeed && touched.signingKeySeed ? ( +
+ {errors.signingKeySeed} +
+ ) : ( +
+ )}
- ) : ( -
- )} -
+
+ +   + +
+ +
+
+ + )} + {activeProofMethodTab === OwnershipProofMethod.keystore && ( + <> +
+ + Signing key seed + + ) => + handleProofOfOwnershipWithKeystore(e, setFieldValue) + } + className={`mt-4 block w-full rounded-full bg-white from-pinkAccent to-purpleDeepAccent px-4 py-[10px] text-sm text-gray-900 shadow-lg dark:bg-gradient-to-r dark:text-white + ${ + errors.signingKeystore && + touched.signingKeystore && + 'block w-full rounded-full bg-white px-4 py-[10px] text-sm text-gray-900 shadow-lg dark:bg-blueDarkAccent' + } + `} + /> + {errors.signingKeystore && touched.signingKeystore ? ( +
+ {errors.signingKeystore} +
+ ) : ( +
+ )} +
+ + )} + {values.signingKey && values.signature && ( + <> +
+ + Signing key + + + {errors.signingKey && touched.signingKey ? ( +
+ {errors.signingKey} +
+ ) : ( +
+ )} +
+ +
+ + Proof of signing key ownership signature + + + {errors.signature && touched.signature ? ( +
+ {errors.signature} +
+ ) : ( +
+ )} +
+ + )}
- Amount to Stake + Amount to Stake ({selectedChain.token.symbol}) {
- Nominator tax + Nominator tax (%) {
- Minimum Nominator Stake + Minimum Nominator Stake ({selectedChain.token.symbol}) { @@ -112,7 +111,7 @@ export const OperatorsList: FC = () => { cell: ({ row, }: Cell) => ( -
{`${bigNumberToNumber(row.original.minimumNominatorStake)} tSSC`}
+
{`${bigNumberToNumber(row.original.minimumNominatorStake)} ${selectedChain.token.symbol}`}
), }, { @@ -132,7 +131,7 @@ export const OperatorsList: FC = () => { cell: ({ row, }: Cell) => ( -
{`${bigNumberToNumber(row.original.currentTotalStake)} tSSC`}
+
{`${bigNumberToNumber(row.original.currentTotalStake)} ${selectedChain.token.symbol}`}
), }, { @@ -201,7 +200,14 @@ export const OperatorsList: FC = () => { }, }) return cols - }, [subspaceAccount, selectedChain.urls.page, selectedDomain, action, handleAction]) + }, [ + subspaceAccount, + selectedChain.urls.page, + selectedChain.token.symbol, + selectedDomain, + action, + handleAction, + ]) const orderBy = useMemo(() => sort(sorting, 'id_ASC'), [sorting]) @@ -267,11 +273,11 @@ export const OperatorsList: FC = () => {
-
{`Operators (${totalLabel})`}
+
{`Operators (${totalLabel})`}
{ filename='operators-operators-list' pageSizeOptions={[10]} fullDataDownloader={fullDataDownloader} - mobileComponent={ - - } />
@@ -307,47 +305,3 @@ export const OperatorsList: FC = () => {
) } - -type MobileComponentProps = { - operators: OperatorsConnectionQuery['operatorsConnection']['edges'][0]['node'][] - action: OperatorAction - handleAction: (value: OperatorAction) => void - subspaceAccount?: string -} - -const MobileComponent: FC = ({ - operators, - action, - handleAction, - subspaceAccount, -}) => ( -
- {operators.map((operator, index) => { - const nominator = - subspaceAccount && - operator.nominators.find( - (nominator) => nominator.id === `${operator.id}-${subspaceAccount}`, - ) - return ( - - ) - })} -
-) diff --git a/explorer/src/components/Operator/OperatorsListCard.tsx b/explorer/src/components/Operator/OperatorsListCard.tsx deleted file mode 100644 index 989ce583e..000000000 --- a/explorer/src/components/Operator/OperatorsListCard.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { bigNumberToNumber, numberWithCommas } from '@/utils/number' -import { shortString } from '@/utils/string' -import { MobileCard, Row } from 'components/common/MobileCard' -import { Chains } from 'constants/' -import { INTERNAL_ROUTES } from 'constants/routes' -import { OperatorsConnectionQuery } from 'gql/graphql' -import useDomains from 'hooks/useDomains' -import useWallet from 'hooks/useWallet' -import Link from 'next/link' -import { FC, useMemo } from 'react' -import { operatorStatus } from 'utils/operator' -import { capitalizeFirstLetter } from 'utils/string' -import { ActionsDropdown } from './ActionsDropdown' -import { OperatorAction, OperatorActionType } from './ActionsModal' - -type Props = { - operator: OperatorsConnectionQuery['operatorsConnection']['edges'][0]['node'] - action: OperatorAction - handleAction: (value: OperatorAction) => void - index: number - excludeActions?: OperatorActionType[] - nominatorMaxShares?: bigint - lastBlock?: number -} - -export const OperatorsListCard: FC = ({ - operator, - action, - handleAction, - excludeActions, - nominatorMaxShares, - lastBlock, -}) => { - const { selectedChain, selectedDomain } = useDomains() - const { actingAccount } = useWallet() - - const body = useMemo(() => { - const rows: Row[] = [ - { name: 'Domain', value: operator.currentDomainId === 0 ? 'Subspace' : 'Nova' }, - { name: 'Signing Key', value: shortString(operator.signingKey) }, - { name: 'Owner', value: shortString(operator.operatorOwner || '') }, - { - name: 'Minimum Stake', - value: `${bigNumberToNumber(operator.minimumNominatorStake)} tSSC`, - }, - { name: 'Nominator Tax', value: `${operator.nominationTax}%` }, - { name: 'Total Stake', value: `${bigNumberToNumber(operator.currentTotalStake)} tSSC` }, - { name: 'Total Shares', value: numberWithCommas(operator.totalShares) }, - { - name: 'Status', - value: operator.status - ? selectedChain.urls.page === Chains.gemini3g - ? operator.status - : capitalizeFirstLetter(operatorStatus(operator.status, lastBlock)) - : 'unknown', - }, - ] - if (actingAccount) - rows.push({ - name: 'Actions', - value: ( - - ), - }) - return rows - }, [ - actingAccount, - action, - lastBlock, - excludeActions, - handleAction, - nominatorMaxShares, - operator.currentDomainId, - operator.currentTotalStake, - operator.id, - operator.minimumNominatorStake, - operator.nominationTax, - operator.operatorOwner, - operator.signingKey, - operator.status, - operator.totalShares, - selectedChain.urls.page, - ]) - - return ( - -

- {operator.id} -

- - } - body={body} - /> - ) -} diff --git a/explorer/src/components/StakeWars/NominatorList.tsx b/explorer/src/components/StakeWars/NominatorList.tsx index c09d5ced8..5a1c1587a 100644 --- a/explorer/src/components/StakeWars/NominatorList.tsx +++ b/explorer/src/components/StakeWars/NominatorList.tsx @@ -16,7 +16,6 @@ import type { Cell } from 'types/table' import { downloadFullData } from 'utils/downloadFullData' import { sort } from 'utils/sort' import { NotStarted } from '../layout/NotStarted' -import { NominatorListCard } from './NominatorListCard' import { NominatorWithRewards, getNominatorRewards } from './helpers/calculateNominatorReward' type Props = { @@ -170,7 +169,7 @@ export const NominatorList: FC = ({ currentBlock }) => {
-
{`Nominators (${totalLabel})`}
+
{`Nominators (${totalLabel})`}
@@ -188,7 +187,6 @@ export const NominatorList: FC = ({ currentBlock }) => { onPaginationChange={setPagination} filename='stake-wars-nominator-list' fullDataDownloader={fullDataDownloader} - mobileComponent={} /> )}
@@ -196,21 +194,3 @@ export const NominatorList: FC = ({ currentBlock }) => {
) } - -type MobileComponentProps = { - nominators: GetAllNominatorsQuery['nominatorsConnection']['edges'][0]['node'][] | undefined -} - -const MobileComponent: FC = ({ nominators }) => ( -
- {nominators?.map((nominator, index) => { - return ( - - ) - })} -
-) diff --git a/explorer/src/components/StakeWars/OperatorListCard.tsx b/explorer/src/components/StakeWars/OperatorListCard.tsx index 36959172c..5d698a2cd 100644 --- a/explorer/src/components/StakeWars/OperatorListCard.tsx +++ b/explorer/src/components/StakeWars/OperatorListCard.tsx @@ -22,10 +22,13 @@ export const OperatorsListCard: FC = ({ operator }) => { { name: 'Owner', value: shortString(operator.operatorOwner || '') }, { name: 'Minimum Stake', - value: `${bigNumberToNumber(operator.minimumNominatorStake)} tSSC`, + value: `${bigNumberToNumber(operator.minimumNominatorStake)} ${selectedChain.token.symbol}`, }, { name: 'Nominator Tax', value: `${operator.nominationTax}%` }, - { name: 'Total Stake', value: `${bigNumberToNumber(operator.currentTotalStake)} tSSC` }, + { + name: 'Total Stake', + value: `${bigNumberToNumber(operator.currentTotalStake)} ${selectedChain.token.symbol}`, + }, { name: 'Total Shares', value: numberWithCommas(operator.totalShares) }, { name: 'Status', value: operator.status ? operator.status : 'unknown' }, ] @@ -39,6 +42,7 @@ export const OperatorsListCard: FC = ({ operator }) => { operator.signingKey, operator.status, operator.totalShares, + selectedChain.token.symbol, ]) return ( @@ -55,7 +59,7 @@ export const OperatorsListCard: FC = ({ operator }) => { operator.id, )} > -

+

{operator.id}

diff --git a/explorer/src/components/StakeWars/OperatorsList.tsx b/explorer/src/components/StakeWars/OperatorsList.tsx index 3587a257e..52407a503 100644 --- a/explorer/src/components/StakeWars/OperatorsList.tsx +++ b/explorer/src/components/StakeWars/OperatorsList.tsx @@ -5,7 +5,6 @@ import { bigNumberToNumber, numberWithCommas } from '@/utils/number' import { shortString } from '@/utils/string' import { useApolloClient, useQuery } from '@apollo/client' import { SortingState } from '@tanstack/react-table' -import { OperatorsListCard } from 'components/StakeWars/OperatorListCard' import { NewTable } from 'components/common/NewTable' import { Spinner } from 'components/common/Spinner' import { NotFound } from 'components/layout/NotFound' @@ -79,7 +78,7 @@ export const OperatorsList: FC = ({ currentBlock }) => { header: 'Min. Stake', enableSorting: true, cell: ({ row }: Cell) => ( -
{`${bigNumberToNumber(row.original.minimumNominatorStake)} tSSC`}
+
{`${bigNumberToNumber(row.original.minimumNominatorStake)} ${selectedChain.token.symbol}`}
), }, { @@ -87,7 +86,7 @@ export const OperatorsList: FC = ({ currentBlock }) => { header: 'Total Stake', enableSorting: true, cell: ({ row }: Cell) => ( -
{`${bigNumberToNumber(row.original.currentTotalStake)} tSSC`}
+
{`${bigNumberToNumber(row.original.currentTotalStake)} ${selectedChain.token.symbol}`}
), }, { @@ -99,12 +98,12 @@ export const OperatorsList: FC = ({ currentBlock }) => { }: Cell< GetAllOperatorsQuery['operatorsConnection']['edges'][0]['node'] & { rewards: bigint } >) => ( -
{`${row.original.rewards ? bigNumberToNumber(row.original.rewards.toString()) : 0} tSSC`}
+
{`${row.original.rewards ? bigNumberToNumber(row.original.rewards.toString()) : 0} ${selectedChain.token.symbol}`}
), }, ] return cols - }, [selectedChain.urls.page, selectedDomain]) + }, [selectedChain.token.symbol, selectedChain.urls.page, selectedDomain]) const orderBy = useMemo(() => sort(sorting, 'id_ASC'), [sorting]) @@ -176,7 +175,7 @@ export const OperatorsList: FC = ({ currentBlock }) => {
-
{`Operators (${totalLabel})`}
+
{`Operators (${totalLabel})`}
@@ -193,28 +192,9 @@ export const OperatorsList: FC = ({ currentBlock }) => { onPaginationChange={setPagination} filename='stake-wars-operators-list' fullDataDownloader={fullDataDownloader} - mobileComponent={} />
) } - -type MobileComponentProps = { - operators: GetAllOperatorsQuery['operatorsConnection']['edges'][0]['node'][] -} - -const MobileComponent: FC = ({ operators }) => ( -
- {operators.map((operator, index) => { - return ( - - ) - })} -
-) diff --git a/explorer/src/components/WalletSideKick/AccountHeader.tsx b/explorer/src/components/WalletSideKick/AccountHeader.tsx index a24d79927..1335b6046 100644 --- a/explorer/src/components/WalletSideKick/AccountHeader.tsx +++ b/explorer/src/components/WalletSideKick/AccountHeader.tsx @@ -1,33 +1,36 @@ import { limitNumberDecimals } from '@/utils/number' import { CopyButton } from 'components/common/CopyButton' import { Tooltip } from 'components/common/Tooltip' +import useWallet from 'hooks/useWallet' import { FC } from 'react' import { ActionsButtons } from './ActionsButtons' interface AccountHeaderProps { - subspaceAccount: string walletBalance: number tokenSymbol: string } -export const AccountHeader: FC = ({ - subspaceAccount, - walletBalance, - tokenSymbol, -}) => { +export const AccountHeader: FC = ({ walletBalance, tokenSymbol }) => { + const { actingAccount, subspaceAccount } = useWallet() + + if (!actingAccount) return null + return ( <>
- +
diff --git a/explorer/src/components/WalletSideKick/AccountSummary.tsx b/explorer/src/components/WalletSideKick/AccountSummary.tsx index 7d563ae9a..bef55176c 100644 --- a/explorer/src/components/WalletSideKick/AccountSummary.tsx +++ b/explorer/src/components/WalletSideKick/AccountSummary.tsx @@ -28,7 +28,7 @@ export const AccountSummary: FC = ({ const theme = selectedChain.isDomain ? 'ethereum' : 'beachball' return ( -
+
= ({
- + {actingAccountName} - + {shortString(subspaceAccount)}
@@ -73,19 +73,19 @@ export const AccountSummary: FC = ({ /> )}
- + Your Subspace Wallet Address
{subspaceAccount && ( - + {subspaceAccount} )}
- + Your Subspace Wallet Balance
diff --git a/explorer/src/components/WalletSideKick/ActionsModal.tsx b/explorer/src/components/WalletSideKick/ActionsModal.tsx index ebbae4190..43bca5abd 100644 --- a/explorer/src/components/WalletSideKick/ActionsModal.tsx +++ b/explorer/src/components/WalletSideKick/ActionsModal.tsx @@ -11,6 +11,7 @@ import { AMOUNT_TO_SUBTRACT_FROM_MAX_AMOUNT, ExtrinsicsSupportedModule, WalletAction, + WalletType, } from 'constants/wallet' import { Field, FieldArray, Form, Formik, FormikState } from 'formik' import useDomains from 'hooks/useDomains' @@ -154,7 +155,7 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) }, [api]) const loadWalletBalance = useCallback(async () => { - if (!actingAccount || !api) return + if (!actingAccount || !api || actingAccount.type === WalletType.ethereum) return const balance = await api.query.system.account(actingAccount.address) setWalletBalance( @@ -177,6 +178,7 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) ) => { if (!actingAccount || !injector || !api) return setFormError('We are not able to connect to the blockchain') + if (!subspaceAccount) throw new Error('No subspace account') try { const hash = await api.tx.balances .transferKeepAlive( @@ -202,7 +204,7 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) }) } }, - [api, actingAccount, injector, tokenDecimals], + [actingAccount, injector, api, subspaceAccount, tokenDecimals], ) const handleSignMessage = useCallback( @@ -212,6 +214,7 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) ) => { if (!actingAccount || !injector) return setFormError('We are not able to connect to the blockchain') + if (!subspaceAccount) throw new Error('No subspace account') try { const signature = injector.signer.signRaw && @@ -238,7 +241,7 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) }) } }, - [actingAccount, injector], + [actingAccount, injector, subspaceAccount], ) const handleSendRemark = useCallback( @@ -248,6 +251,7 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) ) => { if (!actingAccount || !injector || !api) return setFormError('We are not able to connect to the blockchain') + if (!subspaceAccount) throw new Error('No subspace account') try { const hash = await api.tx.system .remark(values.message) @@ -270,7 +274,7 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) }) } }, - [actingAccount, api, injector], + [actingAccount, api, injector, subspaceAccount], ) const handleCustomExtrinsic = useCallback( @@ -280,6 +284,7 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) ) => { if (!actingAccount || !injector || !api) return setFormError('We are not able to connect to the blockchain') + if (!subspaceAccount) throw new Error('No subspace account') if (!selectedCategory) return setFormError('You need to select a category') if (!selectedMethod) return setFormError('You need to select a method') try { @@ -305,7 +310,15 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) }) } }, - [actingAccount, api, injector, selectedCategory, selectedMethod, resetCategory], + [ + actingAccount, + injector, + api, + subspaceAccount, + selectedCategory, + selectedMethod, + resetCategory, + ], ) const handleCopy = useCallback((value: string) => { @@ -344,18 +357,18 @@ export const ActionsModal: FC = ({ isOpen, action, onClose }) {result ? ( <> {hash && WalletAction[action] === WalletAction.SendToken && ( - + Extrinsic Hash )}