diff --git a/packages/extension-polkagate/src/popup/history/Detail.tsx b/packages/extension-polkagate/src/popup/history/Detail.tsx index 5de3c1536..13c463a47 100644 --- a/packages/extension-polkagate/src/popup/history/Detail.tsx +++ b/packages/extension-polkagate/src/popup/history/Detail.tsx @@ -9,6 +9,7 @@ import { Divider, Grid, Typography } from '@mui/material'; import React, { useCallback, useContext, useMemo } from 'react'; import { AccountContext, PButton, Popup } from '../../components'; +import { getVoteType } from '../../fullscreen/governance/utils/util'; import { useTranslation } from '../../hooks'; import { HeaderBrand } from '../../partials'; import { accountName, amountToMachine, toShortAddress, upperCaseFirstChar } from '../../util/utils'; @@ -63,7 +64,7 @@ export default function Detail ({ chainName, decimal, info, setShowDetail, showD showBackArrow text={t('Transaction Detail')} /> - + {action} @@ -79,9 +80,24 @@ export default function Detail ({ chainName, decimal, info, setShowDetail, showD {info?.to && } toCopy={info?.to?.address} /> } + {info?.refId && + + } + {info?.refId && + + } + {info?.delegatee && + } noDivider toCopy={info.delegatee} /> + } {info?.amount && } + {info?.conviction && + + } + {info?.class && + + } {info?.fee && } diff --git a/packages/extension-polkagate/src/popup/history/HistoryTabs.tsx b/packages/extension-polkagate/src/popup/history/HistoryTabs.tsx index ee4b63107..15bb5f662 100644 --- a/packages/extension-polkagate/src/popup/history/HistoryTabs.tsx +++ b/packages/extension-polkagate/src/popup/history/HistoryTabs.tsx @@ -6,18 +6,26 @@ import { Box, Divider, Tab, Tabs } from '@mui/material'; import React, { useCallback } from 'react'; -import { useInfo, useTranslation } from '../../hooks'; -import { STAKING_CHAINS } from '../../util/constants'; +import { useInfo, useIsExtensionPopup, useTranslation } from '../../hooks'; +import { GOVERNANCE_CHAINS, STAKING_CHAINS } from '../../util/constants'; export enum TAB_MAP { ALL, TRANSFERS, - STAKING + STAKING, + GOVERNANCE } -export default function HistoryTabs ({ address, setTabIndex, tabIndex }: {address: string | undefined , tabIndex: TAB_MAP, setTabIndex: React.Dispatch>}): React.ReactElement { +interface HistoryTabsProps { + address: string | undefined; + tabIndex: TAB_MAP; + setTabIndex: React.Dispatch>; +} + +export default function HistoryTabs ({ address, setTabIndex, tabIndex }: HistoryTabsProps): React.ReactElement { const { t } = useTranslation(); const { chain } = useInfo(address); + const isExtensionMode = useIsExtensionPopup(); const handleTabChange = useCallback((_event: React.SyntheticEvent, value: number) => { setTabIndex(value); @@ -34,14 +42,15 @@ export default function HistoryTabs ({ address, setTabIndex, tabIndex }: {addres fontWeight: 500 }, color: 'text.primary', - fontSize: '18px', + fontSize: isExtensionMode ? '16px' : '18px', fontWeight: 400, - minWidth: '108px', + minWidth: isExtensionMode ? '70px' : '108px', + p: isExtensionMode ? '12px' : undefined, textTransform: 'capitalize' }} value={TAB_MAP.ALL} /> - } label='' sx={{ minWidth: '1px', p: '0', width: '1px' }} value={4} /> + } label='' sx={{ minWidth: '1px', p: '0', width: '1px' }} value={4} /> {STAKING_CHAINS.includes(chain?.genesisHash ?? '') && - } label='' sx={{ minWidth: '1px', p: '0', width: '1px' }} value={5} /> + } label='' sx={{ minWidth: '1px', p: '0', width: '1px' }} value={5} /> } {STAKING_CHAINS.includes(chain?.genesisHash ?? '') && } + {GOVERNANCE_CHAINS.includes(chain?.genesisHash ?? '') && + } label='' sx={{ minWidth: '1px', p: '0', width: '1px' }} value={6} /> + } + {GOVERNANCE_CHAINS.includes(chain?.genesisHash ?? '') && + + } ); diff --git a/packages/extension-polkagate/src/popup/history/index.tsx b/packages/extension-polkagate/src/popup/history/index.tsx index f4f090ff9..12eb60b14 100644 --- a/packages/extension-polkagate/src/popup/history/index.tsx +++ b/packages/extension-polkagate/src/popup/history/index.tsx @@ -24,9 +24,9 @@ export default function TransactionHistory (): React.ReactElement { const [tabIndex, setTabIndex] = useState(state?.tabIndex ?? TAB_MAP.ALL); - const { grouped, tabHistory, transfersTx } = useTransactionHistory(address, tabIndex); + const { governanceTx, grouped, tabHistory, transfersTx } = useTransactionHistory(address, tabIndex); - const _onBack = useCallback(() => { + const onBack = useCallback(() => { history.push({ pathname: state?.pathname ?? '/' }); @@ -35,7 +35,7 @@ export default function TransactionHistory (): React.ReactElement { return ( <> @@ -63,12 +63,12 @@ export default function TransactionHistory (): React.ReactElement { /> )); })} - {grouped === null && transfersTx.isFetching === false && + {grouped === null && transfersTx.isFetching === false && governanceTx.isFetching === false && {t('Nothing to show')} } - {(grouped === undefined || (transfersTx.isFetching && tabHistory?.length === 0)) && + {(grouped === undefined || ((transfersTx.isFetching || governanceTx.isFetching) && tabHistory?.length === 0)) && }
diff --git a/packages/extension-polkagate/src/popup/history/modal/HistoryDetailModal.tsx b/packages/extension-polkagate/src/popup/history/modal/HistoryDetailModal.tsx index 9ea5a0e21..0bdfd8410 100644 --- a/packages/extension-polkagate/src/popup/history/modal/HistoryDetailModal.tsx +++ b/packages/extension-polkagate/src/popup/history/modal/HistoryDetailModal.tsx @@ -9,6 +9,7 @@ import { Divider, Grid, Typography } from '@mui/material'; import React, { useCallback, useContext, useMemo } from 'react'; import { AccountContext, PButton } from '../../../components'; +import { getVoteType } from '../../../fullscreen/governance/utils/util'; import { useTranslation } from '../../../hooks'; import { accountName, amountToMachine, toShortAddress, upperCaseFirstChar } from '../../../util/utils'; import Explorer from '../Explorer'; @@ -30,7 +31,7 @@ export default function HistoryDetailModal ({ chainName, decimal, info, setShowD const { accounts } = useContext(AccountContext); const options = { day: 'numeric', hour: 'numeric', minute: 'numeric', month: 'short', second: 'numeric', weekday: 'short', year: 'numeric' } as Intl.DateTimeFormatOptions; - const _onBack = useCallback(() => { + const onBack = useCallback(() => { setShowDetail(false); }, [setShowDetail]); @@ -73,9 +74,24 @@ export default function HistoryDetailModal ({ chainName, decimal, info, setShowD {info?.to && } toCopy={info?.to?.address} /> } + {info?.refId && + + } + {info?.refId && + + } + {info?.delegatee && + } noDivider toCopy={info.delegatee} /> + } {info?.amount && } + {info?.conviction && + + } + {info?.class && + + } {info?.fee && } @@ -95,9 +111,9 @@ export default function HistoryDetailModal ({ chainName, decimal, info, setShowD ('Back')} + text={t('Back')} /> ); diff --git a/packages/extension-polkagate/src/popup/history/modal/HistoryModal.tsx b/packages/extension-polkagate/src/popup/history/modal/HistoryModal.tsx index 771dff20b..2cc2ad4f1 100644 --- a/packages/extension-polkagate/src/popup/history/modal/HistoryModal.tsx +++ b/packages/extension-polkagate/src/popup/history/modal/HistoryModal.tsx @@ -32,7 +32,7 @@ export default function HistoryModal ({ address, setDisplayPopup }: Props): Reac const [detailInfo, setDetailInfo] = useState(); const [showDetail, setShowDetail] = useState(false); - const { grouped, tabHistory, transfersTx } = useTransactionHistory(address, tabIndex); + const { governanceTx, grouped, tabHistory, transfersTx } = useTransactionHistory(address, tabIndex); const backToAccount = useCallback(() => setDisplayPopup(undefined), [setDisplayPopup]); @@ -71,12 +71,12 @@ export default function HistoryModal ({ address, setDisplayPopup }: Props): Reac /> )); })} - {grouped === null && transfersTx.isFetching === false && + {grouped === null && transfersTx.isFetching === false && governanceTx.isFetching === false && {t('Nothing to show')} } - {(grouped === undefined || (transfersTx.isFetching && tabHistory?.length === 0)) && + {(grouped === undefined || ((transfersTx.isFetching || governanceTx.isFetching) && tabHistory?.length === 0)) && } {grouped && diff --git a/packages/extension-polkagate/src/popup/history/partials/HistoryItem.tsx b/packages/extension-polkagate/src/popup/history/partials/HistoryItem.tsx index 053edafa3..d34fc5984 100644 --- a/packages/extension-polkagate/src/popup/history/partials/HistoryItem.tsx +++ b/packages/extension-polkagate/src/popup/history/partials/HistoryItem.tsx @@ -84,7 +84,7 @@ export default function HistoryItem ({ anotherDay, chainName, date, decimal, for - {info.success ? t('Completed') : t('Failed')} + {info.success ? t('Completed') : t('Failed')} diff --git a/packages/extension-polkagate/src/popup/history/useTransactionHistory.tsx b/packages/extension-polkagate/src/popup/history/useTransactionHistory.tsx index 2a37e1faa..5313f4fe3 100644 --- a/packages/extension-polkagate/src/popup/history/useTransactionHistory.tsx +++ b/packages/extension-polkagate/src/popup/history/useTransactionHistory.tsx @@ -3,20 +3,16 @@ /* eslint-disable react/jsx-max-props-per-line */ -import type { TransactionDetail, Transfers } from '../../util/types'; +import type { Extrinsics, TransactionDetail, Transfers } from '../../util/types'; import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react'; import { useInfo } from '../../hooks'; +import { getGovHistory } from '../../util/api/getGovHistory'; import { getTxTransfers } from '../../util/api/getTransfers'; import { STAKING_ACTIONS } from '../../util/constants'; import { getHistoryFromStorage } from '../../util/utils'; - -enum TAB_MAP { - ALL, - TRANSFERS, - STAKING -} +import { TAB_MAP } from './HistoryTabs'; interface RecordTabStatus { pageNum: number, @@ -25,6 +21,13 @@ interface RecordTabStatus { transactions?: Transfers[] } +interface RecordTabStatusGov { + pageNum: number, + isFetching?: boolean, + hasMore?: boolean, + transactions?: Extrinsics[] +} + const SINGLE_PAGE_SIZE = 50; const MAX_PAGE = 4; @@ -37,14 +40,16 @@ const INITIAL_STATE = { export interface TransactionHistoryOutput{ grouped: Record | null | undefined; - tabHistory: TransactionDetail[] | null - transfersTx: object & RecordTabStatus + tabHistory: TransactionDetail[] | null; + transfersTx: object & RecordTabStatus; + governanceTx: object & RecordTabStatusGov; } export default function useTransactionHistory (address: string | undefined, tabIndex: TAB_MAP): TransactionHistoryOutput { - const { chainName, formatted } = useInfo(address); + const { chain, chainName, decimal, formatted } = useInfo(address); - const [fetchedHistoriesFromSubscan, setFetchedHistoriesFromSubscan] = React.useState([]); + const [fetchedTransferHistoriesFromSubscan, setFetchedTransferHistoriesFromSubscan] = React.useState([]); + const [fetchedGovernanceHistoriesFromSubscan, setFetchedGovernanceHistoriesFromSubscan] = React.useState([]); const [tabHistory, setTabHistory] = useState([]); const [localHistories, setLocalHistories] = useState([]); @@ -52,11 +57,18 @@ export default function useTransactionHistory (address: string | undefined, tabI return Object.assign({}, state, action); } + function stateReducerGov (state: object, action: RecordTabStatusGov) { + return Object.assign({}, state, action); + } + const [transfersTx, setTransfersTx] = useReducer(stateReducer, INITIAL_STATE); + const [governanceTx, setGovernanceTx] = useReducer(stateReducerGov, INITIAL_STATE); const observerInstance = useRef(); const receivingTransfers = useRef(); + const receivingGovernance = useRef(); receivingTransfers.current = transfersTx; + receivingGovernance.current = governanceTx; const grouped = useMemo((): Record | null | undefined => { if (!tabHistory) { @@ -83,6 +95,35 @@ export default function useTransactionHistory (address: string | undefined, tabI return temp; }, [tabHistory]); + useEffect(() => { + if (!governanceTx?.transactions?.length || !decimal) { + return; + } + + const govHistoryFromSubscan: TransactionDetail[] = []; + + governanceTx.transactions.forEach((govTx: Extrinsics): void => { + govHistoryFromSubscan.push({ + action: 'Governance', + amount: govTx.amount !== undefined ? (Number(govTx.amount) / (10 ** decimal)).toString() : undefined, + block: govTx.block_num, + class: govTx.class, + conviction: govTx.conviction, + date: govTx.block_timestamp * 1000, // to be consistent with the locally saved times + delegatee: govTx.delegatee, + fee: govTx.fee, + from: { address: govTx.account_display.address, name: '' }, + refId: govTx.refId, + subAction: govTx.call_module_function, + success: govTx.success, + txHash: govTx.extrinsic_hash, + voteType: govTx.voteType + }); + }); + + setFetchedGovernanceHistoriesFromSubscan(govHistoryFromSubscan); + }, [decimal, formatted, governanceTx.transactions, transfersTx]); + useEffect(() => { if (!transfersTx?.transactions?.length) { return; @@ -121,16 +162,16 @@ export default function useTransactionHistory (address: string | undefined, tabI } }); - setFetchedHistoriesFromSubscan(historyFromSubscan); + setFetchedTransferHistoriesFromSubscan(historyFromSubscan); }, [formatted, transfersTx]); useEffect(() => { - if (!localHistories && !fetchedHistoriesFromSubscan) { + if (!localHistories && !fetchedTransferHistoriesFromSubscan && !fetchedGovernanceHistoriesFromSubscan) { return; } - const filteredLocalHistories = localHistories?.filter((h1) => !fetchedHistoriesFromSubscan?.find((h2) => h1.txHash === h2.txHash)); - let history = filteredLocalHistories.concat(fetchedHistoriesFromSubscan); + const filteredLocalHistories = localHistories?.filter((h1) => !fetchedTransferHistoriesFromSubscan?.find((h2) => h1.txHash === h2.txHash)); + let history = filteredLocalHistories.concat(fetchedTransferHistoriesFromSubscan).concat(fetchedGovernanceHistoriesFromSubscan); history = history.sort((a, b) => b.date - a.date); @@ -141,12 +182,15 @@ export default function useTransactionHistory (address: string | undefined, tabI case (TAB_MAP.STAKING): history = history.filter((h) => STAKING_ACTIONS.includes(h.action)); break; + case (TAB_MAP.GOVERNANCE): + history = history.filter((h) => ['Governance', 'Unlock Referenda'].includes(h.action)); + break; default: break; } setTabHistory(history); - }, [tabIndex, fetchedHistoriesFromSubscan, localHistories]); + }, [tabIndex, fetchedTransferHistoriesFromSubscan, localHistories, fetchedGovernanceHistoriesFromSubscan]); useEffect(() => { formatted && getHistoryFromStorage(String(formatted)).then((h) => { @@ -154,6 +198,27 @@ export default function useTransactionHistory (address: string | undefined, tabI }).catch(console.error); }, [formatted, chainName]); + const getGovExtrinsics = useCallback(async (outerState: RecordTabStatusGov): Promise => { + const { pageNum, transactions } = outerState; + + setGovernanceTx({ + isFetching: true, + pageNum + }); + + const res = await getGovHistory(chainName ?? '', String(formatted), pageNum, chain?.ss58Format); + + const { count, extrinsics } = res.data || {}; + const nextPageNum = pageNum + 1; + + setGovernanceTx({ + hasMore: !(nextPageNum * SINGLE_PAGE_SIZE >= count) && nextPageNum < MAX_PAGE, + isFetching: false, + pageNum: nextPageNum, + transactions: transactions?.concat(extrinsics || []) + }); + }, [chainName, formatted, chain?.ss58Format]); + const getTransfers = useCallback(async (outerState: RecordTabStatus): Promise => { const { pageNum, transactions } = outerState; @@ -187,11 +252,11 @@ export default function useTransactionHistory (address: string | undefined, tabI return; // If the observer object is not in view, do nothing } - if (receivingTransfers.current?.isFetching) { + if (receivingTransfers.current?.isFetching && receivingGovernance.current?.isFetching) { return; // If already fetching, do nothing } - if (!receivingTransfers.current?.hasMore) { + if (!receivingTransfers.current?.hasMore && !receivingTransfers.current?.hasMore) { observerInstance.current?.disconnect(); console.log('No more data to load, disconnecting observer.'); @@ -202,6 +267,11 @@ export default function useTransactionHistory (address: string | undefined, tabI .catch((error) => { console.error('Error fetching transfers:', error); }); + + receivingGovernance.current && getGovExtrinsics(receivingGovernance.current) // Fetch more governance history if available + .catch((error) => { + console.error('Error fetching transfers:', error); + }); }; const options = { @@ -221,7 +291,7 @@ export default function useTransactionHistory (address: string | undefined, tabI return () => { observerInstance.current?.disconnect(); }; - }, [chainName, formatted, getTransfers]); + }, [chainName, formatted, getGovExtrinsics, getTransfers, governanceTx]); - return { grouped, tabHistory, transfersTx }; + return { governanceTx, grouped, tabHistory, transfersTx }; } diff --git a/packages/extension-polkagate/src/util/api/getGovHistory.ts b/packages/extension-polkagate/src/util/api/getGovHistory.ts new file mode 100644 index 000000000..bcecd5fa6 --- /dev/null +++ b/packages/extension-polkagate/src/util/api/getGovHistory.ts @@ -0,0 +1,270 @@ +// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable no-case-declarations */ +/* eslint-disable camelcase */ + +import type { Extrinsics, ExtrinsicsRequest } from '../types'; + +import request from 'umi-request'; + +import { hexToU8a } from '@polkadot/util'; +import { encodeAddress } from '@polkadot/util-crypto'; + +// Common types +interface AccountId { + Id: string; +} + +interface StandardVote { + balance: string; + vote: number; +} + +interface SplitAbstainVote { + abstain: string; + aye: string; + nay: string; +} + +// Parameter types for different convictionvoting actions +interface BaseParam { + value: T; +} + +interface VotesType { + Standard: StandardVote; + SplitAbstain: SplitAbstainVote +} + +type ClassOfParam = BaseParam; + +interface DelegateParams extends Array> { + 0: ClassOfParam; // ClassOf + 1: BaseParam; // delegatee AccountId + 2: BaseParam; // conviction Locked2x + 3: BaseParam; // balance +} + +interface UndelegateParams extends Array> { + 0: ClassOfParam; // ClassOf +} + +interface UnlockParams extends Array> { + 0: ClassOfParam; // ClassOf + 1: BaseParam; // delegatee AccountId +} + +interface VoteParams extends Array> { + 0: BaseParam; // PollIndexOf + 1: BaseParam; +} + +interface RemoveVoteParams extends Array> { + 0: ClassOfParam; // ClassOf + 1: BaseParam; // PollIndexOf +} + +// Function types +// Define a type for the param types mapping +interface ParamTypesMapping { + delegate: DelegateParams; + undelegate: UndelegateParams; + vote: VoteParams; + remove_vote: RemoveVoteParams; + unlock: UnlockParams; +} + +const nullObject = { + code: 0, + data: { + count: 0, + extrinsics: null + }, + generated_at: Date.now(), + message: 'Success' +} as unknown as ExtrinsicsRequest; + +const MODULE = 'convictionvoting'; +const RETRY_DELAY = 1100; // 1.1 second delay +const MAX_RETRIES = 7; +const BATCH_SIZE = 3; +const PAGE_SIZE = 12; + +/** + * Sleep function to create delays between retries + * @param ms Milliseconds to sleep + */ +const sleep = (ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)); + +/** + * Enhanced POST request with retry logic for rate limiting + */ +async function postReq ( + api: string, + data: Record = {}, + option?: Record, + retryCount = 0 +): Promise { + try { + const response = await request.post(api, { data, ...option }) as T; + + return response; + } catch (error) { + if (retryCount < MAX_RETRIES) { + console.log(`Rate limit hit, retrying in ${RETRY_DELAY}ms... (Attempt ${retryCount + 1}/${MAX_RETRIES})`); + await sleep(RETRY_DELAY); + + return postReq(api, data, option, retryCount + 1); + } + + throw error; + } +} + +/** + * Processes an array in batches + * @param array Array to process + * @param batchSize Size of each batch + * @param processor Function to process each batch + */ +async function processBatch (array: T[], batchSize: number, processor: (items: T[]) => Promise): Promise { + const results: T[] = []; + + for (let i = 0; i < array.length; i += batchSize) { + const batch = array.slice(i, i + batchSize); + const batchResults = await processor(batch); + + results.push(...batchResults); + + // Add delay between batches if not the last batch + if (i + batchSize < array.length) { + await sleep(RETRY_DELAY); + } + } + + return results; +} + +/** + * Process a batch of extrinsics + */ +async function processExtrinsicsBatch (extrinsics: Extrinsics[], network: string, prefix: number) { + return Promise.all( + extrinsics.map(async (extrinsic) => { + try { + const functionName = extrinsic.call_module_function as keyof ParamTypesMapping; + + interface ResponseType { + data: { + params: ParamTypesMapping[typeof functionName]; + }; + } + + const txDetail = await postReq( + `https://${network}.api.subscan.io/api/scan/extrinsic`, + { hash: extrinsic.extrinsic_hash } + ); + + const additionalInfo = getAdditionalInfo(functionName, txDetail, prefix); + + return { + ...extrinsic, + ...additionalInfo + } as Extrinsics; + } catch (error) { + console.error('Failed to fetch details for extrinsic:', error); + + return extrinsic; + } + }) + ); +} + +/** + * Fetches governance history for a given address + * @param chainName - Name of the blockchain + * @param address - Account address + * @param pageNum - Page number for pagination + * @param prefix - chain prefix + * @returns Promise resolving to ExtrinsicsRequest + */ +export async function getGovHistory (chainName: string, address: string, pageNum: number, prefix: number | undefined): Promise { + if (!chainName || prefix === undefined) { + return Promise.resolve(nullObject); + } + + const network = chainName.toLowerCase(); + + const extrinsics = await postReq(`https://${network}.api.subscan.io/api/v2/scan/extrinsics`, { + address, + module: MODULE, + page: pageNum, + row: PAGE_SIZE + }); + + if (!extrinsics.data.extrinsics) { + return extrinsics; + } + + // Process extrinsics in batches + const extrinsicsInfo = await processBatch( + extrinsics.data.extrinsics, + BATCH_SIZE, + (batch) => processExtrinsicsBatch(batch, network, prefix) + ); + + return { + ...extrinsics, + data: { + ...extrinsics.data, + extrinsics: extrinsicsInfo + } + }; +} + +function getAdditionalInfo (functionName: keyof ParamTypesMapping, txDetail: { data: { params: ParamTypesMapping[typeof functionName]; } }, prefix: number) { + const id = (txDetail.data.params[1]?.value as AccountId).Id as string | undefined; + const formattedAddress = id ? encodeAddress(hexToU8a(id), prefix) : undefined; + + const voteBalance = ((txDetail.data.params[1]?.value as VotesType)?.Standard?.balance ?? (txDetail.data.params[1]?.value as VotesType)?.SplitAbstain?.abstain) as string | undefined; + const voteType = ((txDetail.data.params[1]?.value as VotesType)?.Standard?.vote ?? null) as number | null; + + switch (functionName) { + case 'delegate': + return { + amount: txDetail.data.params[3]?.value as string | undefined, + class: txDetail.data.params[0]?.value as number | undefined, + conviction: txDetail.data.params[2]?.value as string | undefined, + delegatee: formattedAddress + }; + + case 'undelegate': + return { + class: txDetail.data.params[0]?.value as number | undefined + }; + + case 'unlock': + return { + class: txDetail.data.params[0]?.value as number | undefined, + from: formattedAddress + }; + + case 'vote': + return { + amount: voteBalance, + refId: txDetail.data.params[0]?.value as number | undefined, + voteType + }; + + case 'remove_vote': + return { + class: txDetail.data.params[0]?.value as number | undefined, + refId: txDetail.data.params[1]?.value as number | undefined + }; + + default: + return {}; + } +} diff --git a/packages/extension-polkagate/src/util/types.ts b/packages/extension-polkagate/src/util/types.ts index c66e08260..1ec110c01 100644 --- a/packages/extension-polkagate/src/util/types.ts +++ b/packages/extension-polkagate/src/util/types.ts @@ -1,8 +1,6 @@ // Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors // SPDX-License-Identifier: Apache-2.0 -// @ts-nocheck - import type { LinkOption } from '@polkagate/apps-config/endpoints/types'; import type React from 'react'; import type { ApiPromise } from '@polkadot/api'; @@ -14,6 +12,7 @@ import type { InjectedExtension } from '@polkadot/extension-inject/types'; import type { IconTheme } from '@polkadot/react-identicon/types'; import type { Balance } from '@polkadot/types/interfaces'; import type { AccountId } from '@polkadot/types/interfaces/runtime'; +// @ts-ignore import type { PalletNominationPoolsBondedPoolInner, PalletNominationPoolsPoolMember, PalletNominationPoolsRewardPool } from '@polkadot/types/lookup'; import type { BN } from '@polkadot/util'; import type { KeypairType } from '@polkadot/util-crypto/types'; @@ -97,9 +96,7 @@ export interface ValidatorsIdentities { eraIndex: number; } -export interface SavedValidatorsIdentities { - [chainName: string]: ValidatorsIdentities; -} +export type SavedValidatorsIdentities = Record; export interface AllValidatorsFromSubscan { current: ValidatorsFromSubscan[]; @@ -113,7 +110,7 @@ export interface ValidatorsName { export interface SavedMetaData { chainName: string; - metaData: any + metaData: unknown } export interface ValidatorsFromSubscan { @@ -139,8 +136,8 @@ interface stashAccountDisplay { address: string; display: string; identity: boolean; - judgements: any; - parent: any; + judgements: unknown; + parent: unknown; } export interface TxResult { @@ -151,15 +148,20 @@ export interface TxResult { failureText?: string; } export interface TransactionDetail extends TxResult { - action: string; // send, Solo staking, pool staking ... + action: string; // send, Solo staking, pool staking, convictionvoting ... amount?: string; chain?: Chain; date: number; from: NameAddress; - subAction?: string; // bond_extra, unbound, nominate + subAction?: string; // bond_extra, unbound, nominate, vote to?: NameAddress; token?: string; throughProxy?: NameAddress; + refId?: number; + voteType?: number; + class?: number; + conviction?: string; + delegatee?: string; } export interface TxInfo extends TransactionDetail { @@ -226,7 +228,7 @@ interface Identity { export interface TransferRequest { code: number; data: { - list: any; + list: unknown; count: number; transfers: Transfers[]; }; @@ -234,6 +236,16 @@ export interface TransferRequest { message: string; } +export interface ExtrinsicsRequest { + code: number; + data: { + count: number; + extrinsics: Extrinsics[]; + }; + generated_at: number; + message: string; +} + export interface TipsRequest { code: number; data: { @@ -244,6 +256,32 @@ export interface TipsRequest { message: string; } +export interface Extrinsics { + id: number, + block_num: number, + block_timestamp: number, + extrinsic_index: string, + call_module_function: string, // vote + call_module: string, // convictionvoting + nonce: number, + extrinsic_hash: string, + success: boolean, + fee: string, + fee_used: string, + tip: string, + finalized: true, + account_display: { + address: string, + people: Record + }, + refId?: number; + amount?: string; + voteType?: number; + class?: number; + conviction?: string; + delegatee?: string; +} + export interface Transfers { amount: string; asset_symbol: string; @@ -267,7 +305,7 @@ interface AccountDisplay { judgements: string; account_index: string; identity: boolean; - parent: any; + parent: unknown; } export interface CouncilInfo extends DeriveElectionsInfo { @@ -282,7 +320,7 @@ export interface PersonsInfo { export interface MotionsInfo { proposals: DeriveCollectiveProposal[]; - proposalInfo: any[]; + proposalInfo: unknown[]; accountInfo: DeriveAccountInfo[] } @@ -484,7 +522,7 @@ export interface SubQueryRewardInfo { reward: Reward } export interface SubQueryHistory { - action(action: any): unknown; + action(action: unknown): unknown; id: string, blockNumber: number, extrinsicIdx: number,