diff --git a/packages/app/package.json b/packages/app/package.json index a96aaff910..f7f961ae26 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -21,14 +21,14 @@ "@fortawesome/react-fontawesome": "^0.2.2", "@ledgerhq/hw-transport-webhid": "^6.29.2", "@polkadot-api/merkleize-metadata": "^1.1.4", - "@polkadot/api": "^14.3.1", - "@polkadot/rpc-provider": "^14.3.1", + "@polkadot-api/signers-common": "^0.1.1", + "@polkadot-api/substrate-bindings": "^0.9.3", "@polkawatch/ddp-client": "^2.0.20", "@substrate/connect": "^2.0.1", - "@w3ux/extension-assets": "^0.4.0", + "@w3ux/extension-assets": "^1.0.0-beta.1", "@w3ux/factories": "^1.0.0", "@w3ux/hooks": "^1.3.1-beta.7", - "@w3ux/react-connect-kit": "^1.8.0", + "@w3ux/react-connect-kit": "2.0.0-beta.2", "@w3ux/react-odometer": "^1.1.0", "@w3ux/react-polkicon": "^2.0.1-alpha.0", "@w3ux/utils": "^1.1.1-beta.11", @@ -46,6 +46,7 @@ "i18next-browser-languagedetector": "^8.0.0", "locales": "workspace:*", "plugin-staking-api": "workspace:*", + "polkadot-api": "^1.7.3", "qrcode-generator": "1.4.4", "rc-slider": "^11.1.6", "react": "^18.3.1", @@ -55,6 +56,7 @@ "react-helmet": "^6.1.0", "react-router-dom": "^6.23.1", "react-scroll": "^1.9.0", + "rxjs": "^7.8.1", "styled-components": "^6.1.13", "styles": "workspace:*", "ui-buttons": "workspace:*", diff --git a/packages/app/src/canvas/CreatePool/Summary/index.tsx b/packages/app/src/canvas/CreatePool/Summary/index.tsx index 84a7087bb7..537a0bb0f2 100644 --- a/packages/app/src/canvas/CreatePool/Summary/index.tsx +++ b/packages/app/src/canvas/CreatePool/Summary/index.tsx @@ -10,8 +10,8 @@ import { useApi } from 'contexts/Api'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { useNetwork } from 'contexts/Network'; import { useBondedPools } from 'contexts/Pools/BondedPools'; -import { usePoolMembers } from 'contexts/Pools/PoolMembers'; import { useSetup } from 'contexts/Setup'; +import { ApiController } from 'controllers/Api'; import { useBatchCall } from 'hooks/useBatchCall'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { useOverlay } from 'kits/Overlay/Provider'; @@ -20,16 +20,17 @@ import { Header } from 'library/SetupSteps/Header'; import { MotionContainer } from 'library/SetupSteps/MotionContainer'; import type { SetupStepProps } from 'library/SetupSteps/types'; import { SubmitTx } from 'library/SubmitTx'; +import { Binary } from 'polkadot-api'; import { useTranslation } from 'react-i18next'; import { SummaryWrapper } from './Wrapper'; export const Summary = ({ section }: SetupStepProps) => { const { t } = useTranslation('pages'); const { - api, poolsConfig: { lastPoolId }, } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); const { newBatchCall } = useBatchCall(); @@ -37,7 +38,6 @@ export const Summary = ({ section }: SetupStepProps) => { const { accountHasSigner } = useImportedAccounts(); const { getPoolSetup, removeSetupProgress } = useSetup(); const { activeAccount, activeProxy } = useActiveAccounts(); - const { queryPoolMember, addToPoolMembers } = usePoolMembers(); const { queryBondedPool, addToBondedPools } = useBondedPools(); const poolId = lastPoolId.plus(1); @@ -47,7 +47,8 @@ export const Summary = ({ section }: SetupStepProps) => { const { metadata, bond, roles, nominations } = progress; const getTxs = () => { - if (!activeAccount || !api) { + const { pApi } = ApiController.get(network); + if (!activeAccount || !pApi) { return null; } @@ -58,14 +59,30 @@ export const Summary = ({ section }: SetupStepProps) => { const bondToSubmit = unitToPlanck(bond, units).toString(); const txs = [ - api.tx.nominationPools.create( - bondToSubmit, - roles?.root || activeAccount, - roles?.nominator || activeAccount, - roles?.bouncer || activeAccount - ), - api.tx.nominationPools.nominate(poolId.toString(), targetsToSubmit), - api.tx.nominationPools.setMetadata(poolId.toString(), metadata), + pApi.tx.NominationPools.create({ + amount: BigInt(bondToSubmit.toString()), + root: { + type: 'Id', + value: roles?.root || activeAccount, + }, + nominator: { + type: 'Id', + value: roles?.nominator || activeAccount, + }, + bouncer: { + type: 'Id', + value: roles?.bouncer || activeAccount, + }, + }), + + pApi.tx.NominationPools.nominate({ + pool_id: poolId.toNumber(), + validators: targetsToSubmit, + }), + pApi.tx.NominationPools.set_metadata({ + pool_id: poolId.toNumber(), + metadata: Binary.fromText(metadata), + }), ]; return newBatchCall(txs, activeAccount); }; @@ -82,12 +99,6 @@ export const Summary = ({ section }: SetupStepProps) => { const pool = await queryBondedPool(poolId.toNumber()); addToBondedPools(pool); - // Query and add account to poolMembers list. - const member = await queryPoolMember(activeAccount); - if (member) { - addToPoolMembers(member); - } - // Reset setup progress. removeSetupProgress('pool', activeAccount); }, diff --git a/packages/app/src/canvas/JoinPool/Overview/JoinForm.tsx b/packages/app/src/canvas/JoinPool/Overview/JoinForm.tsx index 194015851b..5ad3bb4765 100644 --- a/packages/app/src/canvas/JoinPool/Overview/JoinForm.tsx +++ b/packages/app/src/canvas/JoinPool/Overview/JoinForm.tsx @@ -4,14 +4,13 @@ import { unitToPlanck } from '@w3ux/utils'; import type BigNumber from 'bignumber.js'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useApi } from 'contexts/Api'; import { useNetwork } from 'contexts/Network'; -import { usePoolMembers } from 'contexts/Pools/PoolMembers'; import type { ClaimPermission } from 'contexts/Pools/types'; import { useSetup } from 'contexts/Setup'; import { defaultPoolProgress } from 'contexts/Setup/defaults'; import { useTransferOptions } from 'contexts/TransferOptions'; import { defaultClaimPermission } from 'controllers/ActivePools/defaults'; +import { ApiController } from 'controllers/Api'; import { useBatchCall } from 'hooks/useBatchCall'; import { useBondGreatestFee } from 'hooks/useBondGreatestFee'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; @@ -23,13 +22,14 @@ import { SubmitTx } from 'library/SubmitTx'; import { planckToUnitBn } from 'library/Utils'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; +import type { AnyApi } from 'types'; import type { OverviewSectionProps } from '../types'; import { JoinFormWrapper } from '../Wrappers'; export const JoinForm = ({ bondedPool }: OverviewSectionProps) => { const { t } = useTranslation(); - const { api } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); const { @@ -42,7 +42,6 @@ export const JoinForm = ({ bondedPool }: OverviewSectionProps) => { const { getSignerWarnings } = useSignerWarnings(); const { getTransferOptions } = useTransferOptions(); const largestTxFee = useBondGreatestFee({ bondFor: 'pool' }); - const { queryPoolMember, addToPoolMembers } = usePoolMembers(); const { pool: { totalPossibleBond }, @@ -74,8 +73,9 @@ export const JoinForm = ({ bondedPool }: OverviewSectionProps) => { // Get transaction for submission. const getTx = () => { + const { pApi } = ApiController.get(network); const tx = null; - if (!api || !claimPermission || !formValid) { + if (!pApi || !claimPermission || !formValid) { return tx; } @@ -83,11 +83,20 @@ export const JoinForm = ({ bondedPool }: OverviewSectionProps) => { !bondValid ? '0' : bond.bond, units ).toString(); - const txs = [api.tx.nominationPools.join(bondToSubmit, bondedPool.id)]; + const txs: AnyApi[] = [ + pApi.tx.NominationPools.join({ + amount: BigInt(bondToSubmit), + pool_id: bondedPool.id, + }), + ]; // If claim permission is not the default, add it to tx. if (claimPermission !== defaultClaimPermission) { - txs.push(api.tx.nominationPools.setClaimPermission(claimPermission)); + txs.push( + pApi.tx.NominationPools.set_claim_permission({ + permission: { type: claimPermission, value: undefined }, + }) + ); } if (txs.length === 1) { @@ -112,12 +121,6 @@ export const JoinForm = ({ bondedPool }: OverviewSectionProps) => { } }, callbackInBlock: async () => { - // Query and add account to poolMembers list - const member = await queryPoolMember(activeAccount); - if (member) { - addToPoolMembers(member); - } - // Reset local storage setup progress setActiveAccountSetup('pool', defaultPoolProgress); }, diff --git a/packages/app/src/canvas/JoinPool/Overview/Stats.tsx b/packages/app/src/canvas/JoinPool/Overview/Stats.tsx index 07d605afe8..634746b3d1 100644 --- a/packages/app/src/canvas/JoinPool/Overview/Stats.tsx +++ b/packages/app/src/canvas/JoinPool/Overview/Stats.tsx @@ -7,6 +7,7 @@ import { MaxEraRewardPointsEras } from 'consts'; import { useApi } from 'contexts/Api'; import { useNetwork } from 'contexts/Network'; import { usePoolPerformance } from 'contexts/Pools/PoolPerformance'; +import { ApiController } from 'controllers/Api'; import { PoolSync } from 'library/PoolSync'; import { StyledLoader } from 'library/PoolSync/Loader'; import { planckToUnitBn } from 'library/Utils'; @@ -23,13 +24,14 @@ export const Stats = ({ }: OverviewSectionProps) => { const { t } = useTranslation('library'); const { + network, networkData: { units, unit, brand: { token: Token }, }, } = useNetwork(); - const { isReady, api } = useApi(); + const { isReady } = useApi(); const { getPoolRewardPoints } = usePoolPerformance(); const poolRewardPoints = getPoolRewardPoints(performanceKey); const rawEraRewardPoints = Object.values( @@ -41,16 +43,17 @@ export const Stats = ({ // Fetches the balance of the bonded pool. const getPoolBalance = async () => { - if (!api) { + const { pApi } = ApiController.get(network); + if (!pApi) { return; } - const balance = ( - await api.call.nominationPoolsApi.pointsToBalance( - bondedPool.id, - rmCommas(bondedPool.points) - ) - ).toString(); + const apiResult = await pApi.apis.NominationPoolsApi.points_to_balance( + bondedPool.id, + BigInt(rmCommas(bondedPool.points)), + { at: 'best' } + ); + const balance = new BigNumber(apiResult?.toString() || 0); if (balance) { setPoolBalance(new BigNumber(balance)); diff --git a/packages/app/src/canvas/ManageNominations/index.tsx b/packages/app/src/canvas/ManageNominations/index.tsx index 0fdd0a2c72..cd2ee2867e 100644 --- a/packages/app/src/canvas/ManageNominations/index.tsx +++ b/packages/app/src/canvas/ManageNominations/index.tsx @@ -6,9 +6,11 @@ import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useApi } from 'contexts/Api'; import { useBonded } from 'contexts/Bonded'; import { useHelp } from 'contexts/Help'; +import { useNetwork } from 'contexts/Network'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { usePrompt } from 'contexts/Prompt'; +import { ApiController } from 'controllers/Api'; import { NotificationsController } from 'controllers/Notifications'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { useOverlay } from 'kits/Overlay/Provider'; @@ -32,8 +34,9 @@ export const ManageNominations = () => { setCanvasStatus, config: { options }, } = useOverlay().canvas; + const { consts } = useApi(); const { openHelp } = useHelp(); - const { consts, api } = useApi(); + const { network } = useNetwork(); const { activePool } = useActivePool(); const { getBondedAccount } = useBonded(); const { activeAccount } = useActiveAccounts(); @@ -90,26 +93,28 @@ export const ManageNominations = () => { // Tx to submit. const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; - if (!valid || !api) { + if (!valid || !pApi) { return tx; } - // Note: `targets` structure differs between staking and pools. - const targetsToSubmit = newNominations.nominations.map((nominee) => - isPool - ? nominee.address - : { - Id: nominee.address, - } - ); - if (isPool) { if (activePool) { - tx = api.tx.nominationPools.nominate(activePool.id, targetsToSubmit); + tx = pApi.tx.NominationPools.nominate({ + pool_id: activePool.id, + validators: newNominations.nominations.map( + (nominee) => nominee.address + ), + }); } } else { - tx = api.tx.staking.nominate(targetsToSubmit); + tx = pApi.tx.Staking.nominate({ + targets: newNominations.nominations.map((nominee) => ({ + type: 'Id', + value: nominee.address, + })), + }); } return tx; }; diff --git a/packages/app/src/canvas/NominatorSetup/Summary/index.tsx b/packages/app/src/canvas/NominatorSetup/Summary/index.tsx index 6aad03169a..7c10ac6048 100644 --- a/packages/app/src/canvas/NominatorSetup/Summary/index.tsx +++ b/packages/app/src/canvas/NominatorSetup/Summary/index.tsx @@ -6,10 +6,10 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { ellipsisFn, unitToPlanck } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useApi } from 'contexts/Api'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { useNetwork } from 'contexts/Network'; import { useSetup } from 'contexts/Setup'; +import { ApiController } from 'controllers/Api'; import { useBatchCall } from 'hooks/useBatchCall'; import { usePayeeConfig } from 'hooks/usePayeeConfig'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; @@ -24,8 +24,8 @@ import { SummaryWrapper } from './Wrapper'; export const Summary = ({ section }: SetupStepProps) => { const { t } = useTranslation('pages'); - const { api } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); const { newBatchCall } = useBatchCall(); @@ -40,28 +40,42 @@ export const Summary = ({ section }: SetupStepProps) => { const { bond, nominations, payee } = progress; const getTxs = () => { - if (!activeAccount || !api) { + const { pApi } = ApiController.get(network); + if (!activeAccount || !pApi) { return null; } - const targetsToSubmit = nominations.map( - ({ address }: { address: string }) => ({ - Id: address, - }) - ); + if (payee.destination === 'Account' && !payee.account) { + return null; + } + + if (payee.destination !== 'Account' && !payee.destination) { + return null; + } const payeeToSubmit = payee.destination === 'Account' ? { - Account: payee.account, + type: 'Account' as const, + value: payee.account as string, } - : payee.destination; + : { + type: payee.destination, + }; const bondToSubmit = unitToPlanck(bond || '0', units).toString(); const txs = [ - api.tx.staking.bond(bondToSubmit, payeeToSubmit), - api.tx.staking.nominate(targetsToSubmit), + pApi.tx.Staking.bond({ + value: BigInt(bondToSubmit), + payee: payeeToSubmit, + }), + pApi.tx.Staking.nominate({ + targets: nominations.map(({ address }: { address: string }) => ({ + type: 'Id', + value: address, + })), + }), ]; return newBatchCall(txs, activeAccount); }; diff --git a/packages/app/src/canvas/PoolMembers/Lists/Default.tsx b/packages/app/src/canvas/PoolMembers/Lists/Default.tsx deleted file mode 100644 index 0e09c9acfd..0000000000 --- a/packages/app/src/canvas/PoolMembers/Lists/Default.tsx +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import type { Sync } from '@w3ux/types'; -import { useApi } from 'contexts/Api'; -import { usePoolMembers } from 'contexts/Pools/PoolMembers'; -import type { PoolMember } from 'contexts/Pools/PoolMembers/types'; -import { List, ListStatusHeader, Wrapper as ListWrapper } from 'library/List'; -import { MotionContainer } from 'library/List/MotionContainer'; -import { Pagination } from 'library/List/Pagination'; -import { ListProvider } from 'library/List/context'; -import { poolMembersPerPage } from 'library/List/defaults'; -import { useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Member } from './Member'; -import type { DefaultMembersListProps } from './types'; - -export const MembersListInner = ({ - pagination, - batchKey, - members: initialMembers, -}: DefaultMembersListProps) => { - const { t } = useTranslation('pages'); - const { isReady, activeEra } = useApi(); - const { fetchPoolMembersMetaBatch } = usePoolMembers(); - - // current page - const [page, setPage] = useState(1); - - // default list of validators - const [membersDefault, setMembersDefault] = - useState(initialMembers); - - // manipulated list (ordering, filtering) of payouts - const [members, setMembers] = useState(initialMembers); - - // is this the initial fetch - const [fetched, setFetched] = useState('unsynced'); - - // pagination - const totalPages = Math.ceil(members.length / poolMembersPerPage); - const pageEnd = page * poolMembersPerPage - 1; - const pageStart = pageEnd - (poolMembersPerPage - 1); - - // get throttled subset or entire list - const listMembers = members.slice(pageStart).slice(0, poolMembersPerPage); - - // handle validator list bootstrapping - const setupMembersList = () => { - setMembersDefault(initialMembers); - setMembers(initialMembers); - fetchPoolMembersMetaBatch(batchKey, initialMembers, false); - setFetched('synced'); - }; - - // Refetch list when list changes. - useEffect(() => { - if (initialMembers !== membersDefault) { - setFetched('unsynced'); - } - }, [initialMembers]); - - // Configure list when network is ready to fetch. - useEffect(() => { - if (isReady && !activeEra.index.isZero() && fetched === 'unsynced') { - setupMembersList(); - } - }, [isReady, fetched, activeEra.index]); - - return !members.length ? null : ( - - - {listMembers.length > 0 && pagination && ( - - )} - {fetched !== 'synced' ? ( - - {t('pools.fetchingMemberList')}... - - ) : ( - - {listMembers.map((member: PoolMember, index: number) => ( - - ))} - - )} - - - ); -}; - -export const MembersList = (props: DefaultMembersListProps) => { - const { selectToggleable } = props; - return ( - - - - ); -}; diff --git a/packages/app/src/canvas/PoolMembers/Lists/types.ts b/packages/app/src/canvas/PoolMembers/Lists/types.ts index 0e66b3a757..9103cd8c34 100644 --- a/packages/app/src/canvas/PoolMembers/Lists/types.ts +++ b/packages/app/src/canvas/PoolMembers/Lists/types.ts @@ -1,18 +1,12 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { AnyJson } from '@w3ux/types'; - export interface MembersListProps { pagination: boolean; batchKey: string; selectToggleable?: boolean; } -export type DefaultMembersListProps = MembersListProps & { - members: AnyJson; -}; - export type FetchpageMembersListProps = MembersListProps & { memberCount: string; }; diff --git a/packages/app/src/canvas/PoolMembers/Members.tsx b/packages/app/src/canvas/PoolMembers/Members.tsx index c87118a6b7..2cfd1d15ec 100644 --- a/packages/app/src/canvas/PoolMembers/Members.tsx +++ b/packages/app/src/canvas/PoolMembers/Members.tsx @@ -4,20 +4,15 @@ import { faBars } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { useNetwork } from 'contexts/Network'; -import { usePlugins } from 'contexts/Plugins'; import { useActivePool } from 'contexts/Pools/ActivePool'; -import { usePoolMembers } from 'contexts/Pools/PoolMembers'; import { useTheme } from 'contexts/Themes'; import { CardWrapper } from 'library/Card/Wrappers'; import { useTranslation } from 'react-i18next'; -import { MembersList as DefaultMemberList } from './Lists/Default'; import { MembersList as FetchPageMemberList } from './Lists/FetchPage'; export const Members = () => { const { t } = useTranslation('pages'); const { mode } = useTheme(); - const { pluginEnabled } = usePlugins(); - const { getMembersOfPoolFromNode } = usePoolMembers(); const { activePool, isOwner, isBouncer } = useActivePool(); const { colors } = useNetwork().networkData; @@ -78,17 +73,7 @@ export const Members = () => { )} - {pluginEnabled('subscan') ? ( - - ) : ( - - )} + ); diff --git a/packages/app/src/canvas/PoolMembers/Prompts/UnbondMember.tsx b/packages/app/src/canvas/PoolMembers/Prompts/UnbondMember.tsx index a77ff3f3ed..aa2ae4931c 100644 --- a/packages/app/src/canvas/PoolMembers/Prompts/UnbondMember.tsx +++ b/packages/app/src/canvas/PoolMembers/Prompts/UnbondMember.tsx @@ -9,6 +9,7 @@ import { useApi } from 'contexts/Api'; import { useNetwork } from 'contexts/Network'; import type { PoolMembership } from 'contexts/Pools/types'; import { usePrompt } from 'contexts/Prompt'; +import { ApiController } from 'controllers/Api'; import { getUnixTime } from 'date-fns'; import { useErasToTimeLeft } from 'hooks/useErasToTimeLeft'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; @@ -32,8 +33,9 @@ export const UnbondMember = ({ member: PoolMembership; }) => { const { t } = useTranslation('modals'); - const { api, consts } = useApi(); + const { consts } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); const { closePrompt } = usePrompt(); @@ -71,8 +73,9 @@ export const UnbondMember = ({ // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; - if (!api || !activeAccount) { + if (!pApi || !activeAccount) { return tx; } // remove decimal errors @@ -80,7 +83,14 @@ export const UnbondMember = ({ !bondValid ? '0' : bond.bond, units ).toString(); - tx = api.tx.nominationPools.unbond(who, bondToSubmit); + + tx = pApi.tx.NominationPools.unbond({ + member_account: { + type: 'Id', + value: who, + }, + unbonding_points: BigInt(bondToSubmit), + }); return tx; }; diff --git a/packages/app/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx b/packages/app/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx index 00b3cdfd5e..78c7917065 100644 --- a/packages/app/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx +++ b/packages/app/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx @@ -10,6 +10,7 @@ import { useNetwork } from 'contexts/Network'; import { usePoolMembers } from 'contexts/Pools/PoolMembers'; import type { PoolMembership } from 'contexts/Pools/types'; import { usePrompt } from 'contexts/Prompt'; +import { ApiController } from 'controllers/Api'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { ModalNotes } from 'kits/Overlay/structure/ModalNotes'; @@ -34,10 +35,11 @@ export const WithdrawMember = ({ }) => { const { t } = useTranslation('modals'); const { + network, networkData: { units, unit }, } = useNetwork(); const { closePrompt } = usePrompt(); - const { api, consts, activeEra } = useApi(); + const { consts, activeEra } = useApi(); const { activeAccount } = useActiveAccounts(); const { removePoolMember } = usePoolMembers(); const { getSignerWarnings } = useSignerWarnings(); @@ -65,11 +67,18 @@ export const WithdrawMember = ({ // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; - if (!valid || !api) { + if (!valid || !pApi) { return tx; } - tx = api.tx.nominationPools.withdrawUnbonded(who, historyDepth.toString()); + tx = pApi.tx.NominationPools.withdraw_unbonded({ + member_account: { + type: 'Id', + value: who, + }, + num_slashing_spans: historyDepth.toNumber(), + }); return tx; }; const submitExtrinsic = useSubmitExtrinsic({ diff --git a/packages/app/src/config/networks.ts b/packages/app/src/config/networks.ts index 1e0705d81f..47528f1cfb 100644 --- a/packages/app/src/config/networks.ts +++ b/packages/app/src/config/networks.ts @@ -17,7 +17,8 @@ export const NetworkList: Networks = { polkadot: { name: 'polkadot', endpoints: { - lightClient: 'polkadot', + lightClientKey: 'polkadot', + lightClient: async () => await import('polkadot-api/chains/polkadot'), defaultRpcEndpoint: 'IBP-GeoDNS1', rpcEndpoints: { 'Automata 1RPC': 'wss://1rpc.io/dot', @@ -72,7 +73,8 @@ export const NetworkList: Networks = { kusama: { name: 'kusama', endpoints: { - lightClient: 'ksmcc3', + lightClientKey: 'ksmcc3', + lightClient: async () => await import('polkadot-api/chains/ksmcc3'), defaultRpcEndpoint: 'IBP-GeoDNS1', rpcEndpoints: { 'Automata 1RPC': 'wss://1rpc.io/ksm', @@ -127,7 +129,8 @@ export const NetworkList: Networks = { westend: { name: 'westend', endpoints: { - lightClient: 'westend2', + lightClientKey: 'westend2', + lightClient: async () => await import('polkadot-api/chains/westend2'), defaultRpcEndpoint: 'IBP-GeoDNS1', rpcEndpoints: { Dwellir: 'wss://westend-rpc.dwellir.com', @@ -187,11 +190,14 @@ export const SystemChainList: Record = { units: 10, unit: 'DOT', endpoints: { - lightClient: 'polkadot_people', + lightClientKey: 'polkadot_people', + lightClient: async () => + await import('polkadot-api/chains/polkadot_people'), rpcEndpoints: { Parity: 'wss://polkadot-people-rpc.polkadot.io', }, }, + relayChain: 'polkadot', }, 'people-kusama': { name: 'people-kusama', @@ -199,11 +205,14 @@ export const SystemChainList: Record = { units: 12, unit: 'KSM', endpoints: { - lightClient: 'kusama_people', + lightClientKey: 'ksmcc3_people', + lightClient: async () => + await import('polkadot-api/chains/ksmcc3_people'), rpcEndpoints: { Parity: 'wss://kusama-people-rpc.polkadot.io', }, }, + relayChain: 'kusama', }, 'people-westend': { name: 'people-westend', @@ -211,10 +220,13 @@ export const SystemChainList: Record = { units: 12, unit: 'WND', endpoints: { - lightClient: 'westend_people', + lightClientKey: 'westend2_people', + lightClient: async () => + await import('polkadot-api/chains/westend2_people'), rpcEndpoints: { Parity: 'wss://westend-people-rpc.polkadot.io', }, }, + relayChain: 'westend', }, }; diff --git a/packages/app/src/config/util.ts b/packages/app/src/config/util.ts new file mode 100644 index 0000000000..60d8691532 --- /dev/null +++ b/packages/app/src/config/util.ts @@ -0,0 +1,45 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { ApiChainType } from 'model/Api/types'; +import type { AnyApi, NetworkName, SystemChainId } from 'types'; +import { NetworkList, SystemChainList } from './networks'; + +// Get the light client metadata for the given chain type and network. +export const getLightClientMetadata = ( + chainType: ApiChainType, + network: NetworkName | SystemChainId +): { + chain: { + key: string; + fn: () => Promise; + }; + relay?: { + key: string; + fn: () => Promise; + }; +} => { + if (chainType === 'relay') { + return { + chain: { + key: NetworkList[network].endpoints.lightClientKey, + fn: NetworkList[network].endpoints.lightClient, + }, + }; + } + + const { relayChain } = SystemChainList[network]; + const relay = NetworkList[relayChain]; + const system = SystemChainList[network]; + + return { + relay: { + key: relay.endpoints.lightClientKey, + fn: relay.endpoints.lightClient, + }, + chain: { + key: system.endpoints.lightClientKey, + fn: system.endpoints.lightClient, + }, + }; +}; diff --git a/packages/app/src/contexts/Api/defaults.ts b/packages/app/src/contexts/Api/defaults.ts index 4cc3c9d941..5dc387ff1c 100644 --- a/packages/app/src/contexts/Api/defaults.ts +++ b/packages/app/src/contexts/Api/defaults.ts @@ -6,22 +6,14 @@ import { stringToU8a } from '@polkadot/util'; import BigNumber from 'bignumber.js'; import type { APIActiveEra, - APIChainState, APIConstants, APIContextInterface, APINetworkMetrics, APIPoolsConfig, APIStakingMetrics, + PapiChainSpecContext, } from 'contexts/Api/types'; -export const defaultChainState: APIChainState = { - chain: null, - version: { - specVersion: 0, - }, - ss58Prefix: 0, -}; - export const defaultConsts: APIConstants = { bondDuration: new BigNumber(0), maxNominations: new BigNumber(0), @@ -43,6 +35,21 @@ export const defaultNetworkMetrics: APINetworkMetrics = { minimumActiveStake: new BigNumber(0), }; +export const defaultChainSpecs: PapiChainSpecContext = { + genesisHash: '', + ss58Format: 0, + tokenDecimals: 0, + tokenSymbol: '', + received: false, + authoringVersion: 0, + implName: '', + implVersion: 0, + specName: '', + specVersion: 0, + stateVersion: 0, + transactionVersion: 0, +}; + export const defaultActiveEra: APIActiveEra = { index: new BigNumber(0), start: new BigNumber(0), @@ -62,7 +69,6 @@ export const defaultPoolsConfig: APIPoolsConfig = { }; export const defaultStakingMetrics: APIStakingMetrics = { - totalNominators: new BigNumber(0), totalValidators: new BigNumber(0), lastReward: new BigNumber(0), lastTotalStake: new BigNumber(0), @@ -74,9 +80,7 @@ export const defaultStakingMetrics: APIStakingMetrics = { }; export const defaultApiContext: APIContextInterface = { - api: null, - peopleApi: null, - chainState: defaultChainState, + chainSpecs: defaultChainSpecs, isReady: false, apiStatus: 'disconnected', peopleApiStatus: 'disconnected', diff --git a/packages/app/src/contexts/Api/index.tsx b/packages/app/src/contexts/Api/index.tsx index 76ab6bda8a..f5ac2c75c3 100644 --- a/packages/app/src/contexts/Api/index.tsx +++ b/packages/app/src/contexts/Api/index.tsx @@ -18,7 +18,6 @@ import type { } from 'model/Api/types'; import { Era } from 'model/Query/Era'; import { NetworkMeta } from 'model/Query/NetworkMeta'; -import { StakingConstants } from 'model/Query/StakingConstants'; import { ActiveEra } from 'model/Subscribe/ActiveEra'; import { BlockNumber } from 'model/Subscribe/BlockNumber'; import { NetworkMetrics } from 'model/Subscribe/NetworkMetrics'; @@ -27,7 +26,7 @@ import { useEventListener } from 'usehooks-ts'; import { defaultActiveEra, defaultApiContext, - defaultChainState, + defaultChainSpecs, defaultConsts, defaultNetworkMetrics, defaultPoolsConfig, @@ -35,13 +34,13 @@ import { } from './defaults'; import type { APIActiveEra, - APIChainState, APIConstants, APIContextInterface, APINetworkMetrics, APIPoolsConfig, APIProviderProps, APIStakingMetrics, + PapiChainSpecContext, } from './types'; export const APIContext = createContext(defaultApiContext); @@ -105,10 +104,6 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { setRpcEndpointState(key); }; - // Store chain state. - const [chainState, setChainState] = - useState(defaultChainState); - // Store network constants. const [consts, setConsts] = useState(defaultConsts); @@ -133,52 +128,31 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { ); const stakingMetricsRef = useRef(stakingMetrics); - // Fetch chain state. Called once `provider` has been initialised. - const onApiReady = async () => { - const { api } = ApiController.get(network); - - const newChainState = await Promise.all([ - api.rpc.system.chain(), - api.consts.system.version, - api.consts.system.ss58Prefix, - ]); - - // Check that chain values have been fetched before committing to state. Could be expanded to - // check supported chains. - if (newChainState.every((c) => !!c?.toHuman())) { - const chain = newChainState[0]?.toString(); - const version = newChainState[1]?.toJSON(); - const ss58Prefix = Number(newChainState[2]?.toString()); - setChainState({ chain, version, ss58Prefix }); - } - - // Assume chain state is correct and bootstrap network consts. - bootstrapNetworkConfig(); - }; + // Store chain specs from PAPI. + const [chainSpecs, setChainSpecs] = + useState(defaultChainSpecs); // Bootstrap app-wide chain state. const bootstrapNetworkConfig = async () => { const apiInstance = ApiController.get(network); - const api = apiInstance.api; + const pApi = apiInstance.pApi; // 1. Fetch network data for bootstrapping app state: - // Get general network constants for staking UI. - const newConsts = await new StakingConstants().fetch(api, network); - // Get active and previous era. - const { activeEra: newActiveEra, previousEra } = await new Era().fetch(api); + const { activeEra: newActiveEra, previousEra } = await new Era( + pApi + ).fetch(); // Get network meta data related to staking and pools. const { networkMetrics: newNetworkMetrics, poolsConfig: newPoolsConfig, stakingMetrics: newStakingMetrics, - } = await new NetworkMeta().fetch(api, newActiveEra, previousEra); + } = await new NetworkMeta(pApi).fetch(newActiveEra, previousEra); // 2. Populate all config state: - setConsts(newConsts); setStateWithRef(newNetworkMetrics, setNetworkMetrics, networkMetricsRef); const { index, start } = newActiveEra; setStateWithRef({ index, start }, setActiveEra, activeEraRef); @@ -220,7 +194,104 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { SubscriptionsController.set(network, 'activeEra', new ActiveEra(network)); }; - // Handle `polkadot-api` events. + const handlePapiReady = async (e: Event) => { + if (isCustomEvent(e)) { + const { + chainType, + genesisHash, + ss58Format, + tokenDecimals, + tokenSymbol, + authoringVersion, + implName, + implVersion, + specName, + specVersion, + stateVersion, + transactionVersion, + } = e.detail; + + if (chainType === 'relay') { + const newChainSpecs: PapiChainSpecContext = { + genesisHash, + ss58Format, + tokenDecimals, + tokenSymbol, + authoringVersion, + implName, + implVersion, + specName, + specVersion, + stateVersion, + transactionVersion, + received: true, + }; + + const api = ApiController.get(network); + const bondingDuration = await api.getConstant( + 'Staking', + 'BondingDuration', + 0 + ); + const sessionsPerEra = await api.getConstant( + 'Staking', + 'SessionsPerEra', + 0 + ); + const maxExposurePageSize = await api.getConstant( + 'Staking', + 'MaxExposurePageSize', + 0 + ); + const historyDepth = await api.getConstant( + 'Staking', + 'HistoryDepth', + 0 + ); + const expectedBlockTime = await api.getConstant( + 'Babe', + 'ExpectedBlockTime', + 0 + ); + const epochDuration = await api.getConstant('Babe', 'EpochDuration', 0); + const existentialDeposit = await api.getConstant( + 'Balances', + 'ExistentialDeposit', + 0 + ); + const fastUnstakeDeposit = await api.getConstant( + 'FastUnstake', + 'Deposit', + 0 + ); + const poolsPalletId = await api.getConstant( + 'NominationPools', + 'PalletId', + new Uint8Array(0), + 'asBytes' + ); + + setChainSpecs({ ...newChainSpecs, received: true }); + + setConsts({ + maxNominations: new BigNumber(16), + bondDuration: new BigNumber(bondingDuration), + sessionsPerEra: new BigNumber(sessionsPerEra), + maxExposurePageSize: new BigNumber(maxExposurePageSize), + historyDepth: new BigNumber(historyDepth), + expectedBlockTime: new BigNumber(expectedBlockTime.toString()), + epochDuration: new BigNumber(epochDuration.toString()), + existentialDeposit: new BigNumber(existentialDeposit.toString()), + fastUnstakeDeposit: new BigNumber(fastUnstakeDeposit.toString()), + poolsPalletId, + }); + + bootstrapNetworkConfig(); + } + } + }; + + // Handle api status events. const handleNewApiStatus = (e: Event) => { if (isCustomEvent(e)) { const { chainType } = e.detail; @@ -251,9 +322,6 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { return; } switch (status) { - case 'ready': - onApiReady(); - break; case 'connecting': setApiStatus('connecting'); break; @@ -434,8 +502,8 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { setRpcEndpoint(initialRpcEndpoint()); // Reset consts and chain state. + setChainSpecs(defaultChainSpecs); setConsts(defaultConsts); - setChainState(defaultChainState); setStateWithRef( defaultNetworkMetrics, setNetworkMetrics, @@ -452,7 +520,7 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { reInitialiseApi(connectionType); }, [network]); - // Call `unsubscribe` on active instnace on unmount. + // Call `unsubscribe` on active instance on unmount. useEffect( () => () => { const instance = ApiController.get(network); @@ -464,6 +532,7 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { // Add event listener for api events and subscription updates. const documentRef = useRef(document); useEventListener('api-status', handleNewApiStatus, documentRef); + useEventListener('papi-ready', handlePapiReady, documentRef); useEventListener( 'new-network-metrics', handleNetworkMetricsUpdate, @@ -480,16 +549,14 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { return ( ( export const useBalances = () => useContext(BalancesContext); export const BalancesProvider = ({ children }: { children: ReactNode }) => { + const { isReady } = useApi(); + const { network } = useNetwork(); const { getBondedAccount } = useBonded(); const { accounts } = useImportedAccounts(); const createPoolAccounts = useCreatePoolAccounts(); - const { api, peopleApi, peopleApiStatus } = useApi(); const { activeAccount, activeProxy } = useActiveAccounts(); const controller = getBondedAccount(activeAccount); @@ -59,7 +62,7 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => { // If a pool membership exists, let `ActivePools` know of pool membership to re-sync pool // details and nominations. - if (api && poolMembership) { + if (isReady && poolMembership) { const { poolId } = poolMembership; const newPools = ActivePoolsController.getformattedPoolItems( address @@ -67,14 +70,12 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => { id: String(poolId), addresses: { ...createPoolAccounts(Number(poolId)) }, }); + + const { pApi: peopleApi } = ApiController.get( + `people-${network}` as SystemChainId + ); if (peopleApi) { - ActivePoolsController.syncPools( - api, - peopleApi, - peopleApiStatus, - address, - newPools - ); + ActivePoolsController.syncPools(network, address, newPools); } } } @@ -82,7 +83,7 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => { // Check whether all accounts have been synced and update state accordingly. const checkBalancesSynced = () => { - if (Object.keys(BalancesController.balances).length === accounts.length) { + if (Object.keys(BalancesController.accounts).length === accounts.length) { SyncController.dispatch('balances', 'complete'); } }; @@ -91,7 +92,11 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => { // payload. const getNonce = (address: MaybeAddress) => { if (address) { - const maybeNonce = BalancesController.balances[address]?.nonce; + const accountBalances = BalancesController.getAccountBalances( + network, + address + ); + const maybeNonce = accountBalances?.balances?.nonce; if (maybeNonce) { return maybeNonce; } diff --git a/packages/app/src/contexts/Balances/types.ts b/packages/app/src/contexts/Balances/types.ts index 66fd06ff03..3031e8dd34 100644 --- a/packages/app/src/contexts/Balances/types.ts +++ b/packages/app/src/contexts/Balances/types.ts @@ -20,10 +20,10 @@ export interface BalancesContextInterface { export type ActiveBalancesState = Record; export interface ActiveBalance { - ledger: Ledger; + ledger: Ledger | undefined; balances: Balances; - payee: PayeeConfig; - poolMembership: PoolMembership; + payee: PayeeConfig | undefined; + poolMembership: PoolMembership | undefined; nominations: Nominations; } diff --git a/packages/app/src/contexts/Bonded/index.tsx b/packages/app/src/contexts/Bonded/index.tsx index 898cd43343..48714bab69 100644 --- a/packages/app/src/contexts/Bonded/index.tsx +++ b/packages/app/src/contexts/Bonded/index.tsx @@ -1,7 +1,6 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { VoidFn } from '@polkadot/api/types'; import { useEffectIgnoreInitial } from '@w3ux/hooks'; import { addedTo, @@ -14,9 +13,13 @@ import { useExternalAccounts } from 'contexts/Connect/ExternalAccounts'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { useOtherAccounts } from 'contexts/Connect/OtherAccounts'; import { useNetwork } from 'contexts/Network'; +import { SubscriptionsController } from 'controllers/Subscriptions'; +import { isCustomEvent } from 'controllers/utils'; +import { Bonded } from 'model/Subscribe/Bonded'; import type { ReactNode } from 'react'; -import { createContext, useContext, useEffect, useRef, useState } from 'react'; -import type { AnyApi, MaybeAddress } from 'types'; +import { createContext, useContext, useRef, useState } from 'react'; +import type { MaybeAddress } from 'types'; +import { useEventListener } from 'usehooks-ts'; import * as defaults from './defaults'; import type { BondedAccount, BondedContextInterface } from './types'; @@ -27,8 +30,8 @@ export const BondedContext = createContext( export const useBonded = () => useContext(BondedContext); export const BondedProvider = ({ children }: { children: ReactNode }) => { + const { isReady } = useApi(); const { network } = useNetwork(); - const { api, isReady } = useApi(); const { accounts } = useImportedAccounts(); const { addExternalAccount } = useExternalAccounts(); const { addOrReplaceOtherAccount } = useOtherAccounts(); @@ -37,8 +40,6 @@ export const BondedProvider = ({ children }: { children: ReactNode }) => { const [bondedAccounts, setBondedAccounts] = useState([]); const bondedAccountsRef = useRef(bondedAccounts); - const unsubs = useRef>({}); - // Handle the syncing of accounts on accounts change. const handleSyncAccounts = () => { // Sync removed accounts. @@ -48,23 +49,21 @@ export const BondedProvider = ({ children }: { children: ReactNode }) => { ]).map(({ address }) => address); removed?.forEach((address) => { - const unsub = unsubs.current[address]; - if (unsub) { - unsub(); - } + SubscriptionsController.remove(network, `bonded-${address}`); }); - - unsubs.current = Object.fromEntries( - Object.entries(unsubs.current).filter(([key]) => !removed.includes(key)) - ); }; // Sync added accounts. const handleAddedAccounts = () => { const added = addedTo(accounts, bondedAccountsRef.current, ['address']); if (added.length) { - // Subscribe to all newly added accounts bonded and nominator status. - added.map(({ address }) => subscribeToBondedAccount(address)); + added.forEach(({ address }) => + SubscriptionsController.set( + network, + `bonded-${address}`, + new Bonded(network, address) + ) + ); } }; @@ -76,58 +75,41 @@ export const BondedProvider = ({ children }: { children: ReactNode }) => { bondedAccountsRef ); }; + handleRemovedAccounts(); handleAddedAccounts(); handleExistingAccounts(); }; - // Subscribe to account, get controller and nominations. - const subscribeToBondedAccount = async (address: string) => { - if (!api) { - return undefined; - } + const getBondedAccount = (address: MaybeAddress) => + bondedAccountsRef.current.find((a) => a.address === address)?.bonded || + null; - const unsub = await api.queryMulti( - [[api.query.staking.bonded, address]], - async ([controller]) => { - const newAccount: BondedAccount = { - address, - }; - - // set account bonded (controller) or null - let newController = controller.unwrapOr(null); - newController = - newController === null - ? null - : (newController.toHuman() as string | null); - newAccount.bonded = newController; - - // add bonded (controller) account as external account if not presently imported - if (newController) { - if (accounts.find((s) => s.address === newController) === undefined) { - const result = addExternalAccount(newController, 'system'); - if (result) { - addOrReplaceOtherAccount(result.account, result.type); - } + // Handle `polkadot-api` events. + const handleNewBondedAccount = (e: Event) => { + if (isCustomEvent(e)) { + const { account } = e.detail; + const { bonded, address } = account; + + // Add bonded (controller) account as external account if not presently imported + if (bonded) { + if (accounts.find((s) => s.address === bonded) === undefined) { + const result = addExternalAccount(bonded, 'system'); + if (result) { + addOrReplaceOtherAccount(result.account, result.type); } } - - // remove stale account if it's already in list. - const newBonded = Object.values(bondedAccountsRef.current) - .filter((a) => a.address !== address) - .concat(newAccount); - - setStateWithRef(newBonded, setBondedAccounts, bondedAccountsRef); } - ); - unsubs.current[address] = unsub; - return unsub; - }; + // Remove stale account if it's already in list. + const newBonded = Object.values(bondedAccountsRef.current) + .filter((a) => a.address !== address) + .concat(account); - const getBondedAccount = (address: MaybeAddress) => - bondedAccountsRef.current.find((a) => a.address === address)?.bonded || - null; + // Update bonded accounts state. + setStateWithRef(newBonded, setBondedAccounts, bondedAccountsRef); + } + }; // Handle accounts sync on connected accounts change. useEffectIgnoreInitial(() => { @@ -136,14 +118,13 @@ export const BondedProvider = ({ children }: { children: ReactNode }) => { } }, [accounts, network, isReady]); - // Unsubscribe from subscriptions on unmount. - useEffect( - () => () => - Object.values(unsubs.current).forEach((unsub) => { - unsub(); - }), - [] + // Handle new bonded account events. + useEventListener( + 'new-bonded-account', + handleNewBondedAccount, + useRef(document) ); + return ( { - const { isReady, api } = useApi(); + const { isReady } = useApi(); const { network, networkData: { ss58 }, @@ -40,7 +40,6 @@ export const ImportedAccountsProvider = ({ const { otherAccounts } = useOtherAccounts(); const { getExtensionAccounts } = useExtensionAccounts(); const { setActiveAccount, setActiveProxy } = useActiveAccounts(); - // Get the imported extension accounts formatted with the current network's ss58 prefix. const extensionAccounts = getExtensionAccounts(ss58); @@ -116,9 +115,9 @@ export const ImportedAccountsProvider = ({ // Keep accounts in sync with `BalancesController`. useEffectIgnoreInitial(() => { - if (api && isReady) { + if (isReady) { BalancesController.syncAccounts( - api, + network, allAccounts.map((a) => a.address) ); } diff --git a/packages/app/src/contexts/FastUnstake/defaults.ts b/packages/app/src/contexts/FastUnstake/defaults.ts index 3002dce838..d1f39320cf 100644 --- a/packages/app/src/contexts/FastUnstake/defaults.ts +++ b/packages/app/src/contexts/FastUnstake/defaults.ts @@ -13,7 +13,7 @@ export const defaultFastUnstakeContext: FastUnstakeContextInterface = { checking: false, meta: defaultMeta, isExposed: null, - queueDeposit: null, - head: null, - counterForQueue: null, + head: undefined, + queueDeposit: undefined, + counterForQueue: undefined, }; diff --git a/packages/app/src/contexts/FastUnstake/index.tsx b/packages/app/src/contexts/FastUnstake/index.tsx index 15d918611d..ccef702f59 100644 --- a/packages/app/src/contexts/FastUnstake/index.tsx +++ b/packages/app/src/contexts/FastUnstake/index.tsx @@ -3,20 +3,28 @@ import { useEffectIgnoreInitial } from '@w3ux/hooks'; import type { AnyJson } from '@w3ux/types'; -import { rmCommas, setStateWithRef } from '@w3ux/utils'; +import { setStateWithRef } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useApi } from 'contexts/Api'; import { useNetwork } from 'contexts/Network'; import { useStaking } from 'contexts/Staking'; import { validateLocalExposure } from 'contexts/Validators/Utils'; +import { ApiController } from 'controllers/Api'; +import { SubscriptionsController } from 'controllers/Subscriptions'; +import { isCustomEvent } from 'controllers/utils'; +import { FastUnstakeConfig } from 'model/Subscribe/FastUnstakeConfig'; +import type { FastUnstakeHead } from 'model/Subscribe/FastUnstakeConfig/types'; +import { FastUnstakeQueue } from 'model/Subscribe/FastUnstakeQueue'; import type { ReactNode } from 'react'; import { createContext, useContext, useEffect, useRef, useState } from 'react'; -import type { AnyApi, MaybeAddress } from 'types'; +import type { MaybeAddress } from 'types'; +import { useEventListener } from 'usehooks-ts'; import Worker from 'workers/stakers?worker'; import { defaultFastUnstakeContext, defaultMeta } from './defaults'; import type { FastUnstakeContextInterface, + FastUnstakeQueueDeposit, LocalMeta, MetaInterface, } from './types'; @@ -34,7 +42,6 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { const { activeAccount } = useActiveAccounts(); const { inSetup, fetchEraStakers, isBonding } = useStaking(); const { - api, consts, isReady, activeEra, @@ -57,19 +64,13 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { const metaRef = useRef(meta); // store fastUnstake queue deposit for user. - const [queueDeposit, setqueueDeposit] = useState(null); - const queueDepositRef = useRef(queueDeposit); + const [queueDeposit, setQueueDeposit] = useState(); // store fastUnstake head. - const [head, setHead] = useState(null); - const headRef = useRef(head); + const [head, setHead] = useState(); // store fastUnstake counter for queue. - const [counterForQueue, setCounterForQueue] = useState(null); - const counterForQueueRef = useRef(counterForQueue); - - // store fastUnstake subscription unsub. - const unsubs = useRef([]); + const [counterForQueue, setCounterForQueue] = useState(); // localStorage key to fetch local metadata. const getLocalkey = (a: MaybeAddress) => `${network}_fast_unstake_${a}`; @@ -77,27 +78,36 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { // check until bond duration eras surpasssed. const checkToEra = activeEra.index.minus(bondDuration); - // Reset state on network or active account change. + // Reset state on active account change. useEffect(() => { + // Reset fast unstake managment state. + setQueueDeposit(undefined); setStateWithRef(false, setChecking, checkingRef); - setStateWithRef(null, setqueueDeposit, queueDepositRef); - setStateWithRef(null, setCounterForQueue, counterForQueueRef); setStateWithRef(null, setIsExposed, isExposedRef); setStateWithRef(defaultMeta, setMeta, metaRef); - unsubs.current = []; - // cancel fast unstake check on network change or account change. - for (const unsub of unsubs.current) { - unsub(); + // Re-subscribe to fast unstake queue. + SubscriptionsController.remove(network, 'fastUnstakeQueue'); + + if (activeAccount) { + SubscriptionsController.set( + network, + 'fastUnstakeQueue', + new FastUnstakeQueue(network, activeAccount) + ); } + }, [activeAccount]); - // Resubscribe to fast unstake queue. - }, [activeAccount, network]); + // Reset state on network change. + useEffect(() => { + setHead(undefined); + setCounterForQueue(undefined); + }, [network]); // Subscribe to fast unstake queue as soon as api is ready. useEffect(() => { if (isReady) { - subscribeToFastUnstakeQueue(); + subscribeToFastUnstakeMeta(); } }, [isReady]); @@ -152,12 +162,6 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { processEligibility(activeAccount, maybeNextEra); } } - - return () => { - for (const unsub of unsubs.current) { - unsub(); - } - }; }, [ inSetup(), isReady, @@ -211,7 +215,6 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { if (exposed) { // Account is exposed - stop checking. - // cancel checking and update exposed state. setStateWithRef(false, setChecking, checkingRef); setStateWithRef(true, setIsExposed, isExposedRef); @@ -219,9 +222,8 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { // successfully checked current era - bondDuration eras. setStateWithRef(false, setChecking, checkingRef); setStateWithRef(false, setIsExposed, isExposedRef); - - // Finished, not exposed. } else { + // Finished, not exposed. // continue checking the next era. checkEra(new BigNumber(era).minus(1)); } @@ -234,7 +236,6 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { if ( era.isLessThan(0) || !bondDuration.isGreaterThan(0) || - !api || !a || checkingRef.current || !activeAccount || @@ -249,10 +250,6 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { // calls service worker to check exppsures for given era. const checkEra = async (era: BigNumber) => { - if (!api) { - return; - } - const exposures = await fetchEraStakers(era.toString()); worker.postMessage({ @@ -267,50 +264,16 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { }; // subscribe to fastUnstake queue - const subscribeToFastUnstakeQueue = async () => { - if (!api) { + const subscribeToFastUnstakeMeta = async () => { + const { pApi } = ApiController.get(network); + if (!pApi) { return; } - - // TODO: Make a `combineLatest` subscription with new event ---------------------- - const subscribeQueue = async (a: MaybeAddress) => { - const u = await api.query.fastUnstake.queue(a, (q: AnyApi) => - setStateWithRef( - new BigNumber(rmCommas(q.unwrapOrDefault(0).toString())), - setqueueDeposit, - queueDepositRef - ) - ); - return u; - }; - const subscribeHead = async () => { - const u = await api.query.fastUnstake.head((result: AnyApi) => { - const h = result.unwrapOrDefault(null).toHuman(); - setStateWithRef(h, setHead, headRef); - }); - return u; - }; - const subscribeCounterForQueue = async () => { - const u = await api.query.fastUnstake.counterForQueue( - (result: AnyApi) => { - const c = result.toHuman(); - setStateWithRef(c, setCounterForQueue, counterForQueueRef); - } - ); - return u; - }; - // -------------------------------------------------------------------------------- - - // Subscribe to queue + head. - - // initiate subscription, add to unsubs. - await Promise.all([ - subscribeQueue(activeAccount), - subscribeHead(), - subscribeCounterForQueue(), - ]).then((u) => { - unsubs.current = u; - }); + SubscriptionsController.set( + network, + 'fastUnstakeMeta', + new FastUnstakeConfig(network) + ); }; // gets any existing fast unstake metadata for an account. @@ -337,6 +300,36 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { return localMetaValidated; }; + // Handle fast unstake meta events. + const handleNewFastUnstakeConfig = (e: Event) => { + if (isCustomEvent(e)) { + const { head: eventHead, counterForQueue: eventCounterForQueue } = + e.detail; + setHead(eventHead); + setCounterForQueue(eventCounterForQueue); + } + }; + + // Handle fast unstake deposit events. + const handleNewFastUnstakeDeposit = (e: Event) => { + if (isCustomEvent(e)) { + const { address, deposit } = e.detail; + setQueueDeposit({ address, deposit: new BigNumber(deposit.toString()) }); + } + }; + + const documentRef = useRef(document); + useEventListener( + 'new-fast-unstake-config', + handleNewFastUnstakeConfig, + documentRef + ); + useEventListener( + 'new-fast-unstake-deposit', + handleNewFastUnstakeDeposit, + documentRef + ); + return ( { const { t } = useTranslation('modals'); - const { chainState } = useApi(); - const { transactionVersion } = chainState.version; + const { chainSpecs } = useApi(); + const { transactionVersion } = chainSpecs; // Store whether a Ledger device task is in progress. const [isExecuting, setIsExecutingState] = useState(false); diff --git a/packages/app/src/contexts/LedgerHardware/static/ledger.ts b/packages/app/src/contexts/LedgerHardware/static/ledger.ts index e061214eb3..80f3b50304 100644 --- a/packages/app/src/contexts/LedgerHardware/static/ledger.ts +++ b/packages/app/src/contexts/LedgerHardware/static/ledger.ts @@ -70,19 +70,21 @@ export class Ledger { static signPayload = async ( app: PolkadotGenericApp, index: number, - payload: AnyJson, - txMetadata: AnyJson + payload: Uint8Array, + txMetadata?: Uint8Array ) => { await this.ensureOpen(); const bip42Path = `m/44'/354'/${index}'/${0}'/${0}'`; - const buff = Buffer.from(txMetadata); + const toSign = Buffer.from(payload); - const result = await app.signWithMetadata( - bip42Path, - payload.toU8a(true), - buff - ); + let result; + if (txMetadata) { + const buff = Buffer.from(txMetadata); + result = await app.signWithMetadata(bip42Path, toSign, buff); + } else { + result = app.sign(bip42Path, toSign); + } await this.ensureClosed(); return result; diff --git a/packages/app/src/contexts/Payouts/index.tsx b/packages/app/src/contexts/Payouts/index.tsx index 84a44e199e..d91d3e618d 100644 --- a/packages/app/src/contexts/Payouts/index.tsx +++ b/packages/app/src/contexts/Payouts/index.tsx @@ -2,12 +2,19 @@ // SPDX-License-Identifier: GPL-3.0-only import type { AnyJson, Sync } from '@w3ux/types'; -import { rmCommas, setStateWithRef } from '@w3ux/utils'; +import { setStateWithRef } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useApi } from 'contexts/Api'; import { useNetwork } from 'contexts/Network'; import { useStaking } from 'contexts/Staking'; +import { ApiController } from 'controllers/Api'; +import { perbillToPercent } from 'library/Utils'; +import { BondedMulti } from 'model/Query/BondedMulti'; +import { ClaimedRewards } from 'model/Query/ClaimedRewards'; +import { ErasRewardPoints } from 'model/Query/ErasRewardPoints'; +import { ErasValidatorReward } from 'model/Query/ErasValidatorReward'; +import { ValidatorPrefs } from 'model/Query/ValidatorPrefs'; import type { ReactNode } from 'react'; import { createContext, useContext, useEffect, useRef, useState } from 'react'; import type { AnyApi } from 'types'; @@ -35,7 +42,7 @@ export const usePayouts = () => useContext(PayoutsContext); export const PayoutsProvider = ({ children }: { children: ReactNode }) => { const { network } = useNetwork(); - const { api, consts, activeEra } = useApi(); + const { consts, activeEra } = useApi(); const { activeAccount } = useActiveAccounts(); const { isNominating, fetchEraStakers } = useStaking(); const { maxExposurePageSize } = consts; @@ -136,7 +143,8 @@ export const PayoutsProvider = ({ children }: { children: ReactNode }) => { // Start pending payout process once exposure data is fetched. const getUnclaimedPayouts = async () => { - if (!api || !activeAccount) { + const { pApi } = ApiController.get(network); + if (!pApi || !activeAccount) { return; } @@ -163,11 +171,15 @@ export const PayoutsProvider = ({ children }: { children: ReactNode }) => { ); // Fetch controllers in order to query ledgers. - const bondedResults = - await api.query.staking.bonded.multi(uniqueValidators); + const uniqueValidatorsMulti: [string][] = uniqueValidators.map((v) => [v]); + const bondedResultsMulti = await new BondedMulti( + pApi, + uniqueValidatorsMulti + ).fetch(); + const validatorControllers: Record = {}; - for (let i = 0; i < bondedResults.length; i++) { - const ctlr = bondedResults[i].unwrapOr(null); + for (let i = 0; i < bondedResultsMulti.length; i++) { + const ctlr = bondedResultsMulti[i] || null; if (ctlr) { validatorControllers[uniqueValidators[i]] = ctlr; } @@ -186,26 +198,28 @@ export const PayoutsProvider = ({ children }: { children: ReactNode }) => { const results = await Promise.all( unclaimedRewardsEntries.map(([era, v]) => - api.query.staking.claimedRewards(era, v) + new ClaimedRewards(pApi, Number(era), v).fetch() ) ); for (let i = 0; i < results.length; i++) { - const pages = results[i].toHuman() || []; + const pages = results[i] || []; const era = unclaimedRewardsEntries[i][0]; const validator = unclaimedRewardsEntries[i][1]; const exposure = getLocalEraExposure(network, era, activeAccount); const exposedPage = exposure?.[validator]?.exposedPage !== undefined - ? String(exposure[validator].exposedPage) + ? Number(exposure[validator].exposedPage) : undefined; // Add to `unclaimedRewards` if payout page has not yet been claimed. - if (!pages.includes(exposedPage)) { - if (unclaimedRewards?.[validator]) { - unclaimedRewards[validator].push(era); - } else { - unclaimedRewards[validator] = [era]; + if (exposedPage) { + if (!pages.includes(exposedPage)) { + if (unclaimedRewards?.[validator]) { + unclaimedRewards[validator].push(era); + } else { + unclaimedRewards[validator] = [era]; + } } } } @@ -230,10 +244,10 @@ export const PayoutsProvider = ({ children }: { children: ReactNode }) => { if (validators.length > 0) { calls.push( Promise.all([ - api.query.staking.erasValidatorReward(era), - api.query.staking.erasRewardPoints(era), + new ErasValidatorReward(pApi, Number(era)).fetch(), + new ErasRewardPoints(pApi, Number(era)).fetch(), ...validators.map((validator: AnyJson) => - api.query.staking.erasValidatorPrefs(era, validator) + new ValidatorPrefs(pApi, Number(era), validator).fetch() ), ]) ); @@ -244,18 +258,22 @@ export const PayoutsProvider = ({ children }: { children: ReactNode }) => { // `unclaimed`: Record>. const unclaimed: UnclaimedPayouts = {}; let i = 0; - for (const [reward, points, ...prefs] of await Promise.all(calls)) { + for (const [reward, eraRewardPoints, ...prefs] of await Promise.all( + calls + )) { const era = Object.keys(unclaimedByEra)[i]; - const eraTotalPayout = new BigNumber(rmCommas(reward.toHuman())); - const eraRewardPoints = points.toHuman(); + const eraTotalPayout = new BigNumber(reward.toString()); const unclaimedValidators = unclaimedByEra[era]; let j = 0; for (const pref of prefs) { - const eraValidatorPrefs = pref.toHuman(); + const eraValidatorPrefs = { + commission: pref.commission, + blocked: pref.blocked, + }; const commission = new BigNumber( - eraValidatorPrefs.commission.replace(/%/g, '') - ).multipliedBy(0.01); + perbillToPercent(eraValidatorPrefs.commission) + ); // Get validator from era exposure data. Falls back no null if it cannot be found. const validator = unclaimedValidators?.[j] || ''; @@ -273,16 +291,19 @@ export const PayoutsProvider = ({ children }: { children: ReactNode }) => { // Calculate the validator's share of total era payout. const totalRewardPoints = new BigNumber( - rmCommas(eraRewardPoints.total) + eraRewardPoints.total.toString() ); const validatorRewardPoints = new BigNumber( - rmCommas(eraRewardPoints.individual?.[validator] || '0') + eraRewardPoints.individual.find( + ([v]: [string]) => v === validator + )?.[1] || '0' ); + const avail = eraTotalPayout .multipliedBy(validatorRewardPoints) .dividedBy(totalRewardPoints); - const valCut = commission.multipliedBy(avail); + const valCut = commission.multipliedBy(0.01).multipliedBy(avail); const unclaimedPayout = total.isZero() ? new BigNumber(0) diff --git a/packages/app/src/contexts/Pools/ActivePool/index.tsx b/packages/app/src/contexts/Pools/ActivePool/index.tsx index 60ba802333..a13b1330f1 100644 --- a/packages/app/src/contexts/Pools/ActivePool/index.tsx +++ b/packages/app/src/contexts/Pools/ActivePool/index.tsx @@ -8,13 +8,14 @@ import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useBalances } from 'contexts/Balances'; import { useNetwork } from 'contexts/Network'; import { ActivePoolsController } from 'controllers/ActivePools'; +import { ApiController } from 'controllers/Api'; import { SyncController } from 'controllers/Sync'; import { useActivePools } from 'hooks/useActivePools'; import { useCreatePoolAccounts } from 'hooks/useCreatePoolAccounts'; import type { ReactNode } from 'react'; -import { createContext, useContext, useMemo, useRef, useState } from 'react'; +import { createContext, useContext, useRef, useState } from 'react'; +import type { SystemChainId } from 'types'; import { useApi } from '../../Api'; -import { useBondedPools } from '../BondedPools'; import { defaultActivePoolContext, defaultPoolRoles } from './defaults'; import type { ActivePoolContextState } from './types'; @@ -25,32 +26,17 @@ export const ActivePoolContext = createContext( export const useActivePool = () => useContext(ActivePoolContext); export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { + const { isReady } = useApi(); const { network } = useNetwork(); const { getPoolMembership } = useBalances(); const { activeAccount } = useActiveAccounts(); const createPoolAccounts = useCreatePoolAccounts(); - const { isReady, api, peopleApi, peopleApiStatus } = useApi(); - const { getAccountPoolRoles, bondedPools } = useBondedPools(); const membership = getPoolMembership(activeAccount); // Determine active pools to subscribe to. Dependencies of `activeAccount`, and `membership` mean // that this object is only recalculated when these values change. - const accountPoolIds = useMemo(() => { - const rollPoolIds: string[] = Object.keys( - getAccountPoolRoles(activeAccount) || {} - ); - - // If a membership subscription has resulted in an update that is inconsistent with - // `bondedPools`, add that role to the list of the account's pool roles. - if ( - membership?.poolId && - !rollPoolIds.includes(String(membership.poolId)) - ) { - rollPoolIds.push(String(membership.poolId)); - } - return rollPoolIds; - }, [activeAccount, bondedPools, membership]); + const accountPoolId = membership?.poolId ? String(membership.poolId) : null; // Store the currently selected active pool for the UI. Should default to the membership pool if // present. Used in event callback, therefore needs an accompanying ref. @@ -64,27 +50,21 @@ export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { // Only listen to the active account's active pools, otherwise return an empty array. NOTE: // `activePoolsRef` is needed to check if the pool has changed after the async call of fetching // pending rewards. - const { getActivePools, activePoolsRef, getPoolNominations } = useActivePools( - { - who: activeAccount, - onCallback: async () => { - // Sync: active pools synced once all account pools have been reported. - if ( - accountPoolIds.length <= - ActivePoolsController.getPools(activeAccount).length - ) { - SyncController.dispatch('active-pools', 'complete'); - } - }, - } - ); + const { getActivePool, activePoolsRef, getPoolNominations } = useActivePools({ + who: activeAccount, + onCallback: async () => { + if (ActivePoolsController.getPool(network, activeAccount)) { + SyncController.dispatch('active-pools', 'complete'); + } + }, + }); // Store the currently active pool's pending rewards for the active account. const [pendingPoolRewards, setPendingPoolRewards] = useState( new BigNumber(0) ); - const activePool = activePoolId ? getActivePools(activePoolId) : null; + const activePool = activePoolId ? getActivePool(activePoolId) : null; const activePoolNominations = activePoolId ? getPoolNominations(activePoolId) @@ -92,21 +72,21 @@ export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { // Sync active pool subscriptions. const syncActivePoolSubscriptions = async () => { - if (api && accountPoolIds.length) { - const newActivePools = accountPoolIds.map((pool) => ({ - id: pool, - addresses: { ...createPoolAccounts(Number(pool)) }, - })); + if (isReady && accountPoolId) { + const newActivePool = [ + { + id: accountPoolId, + addresses: { ...createPoolAccounts(Number(accountPoolId)) }, + }, + ]; SyncController.dispatch('active-pools', 'syncing'); + const { pApi: peopleApi } = ApiController.get( + `people-${network}` as SystemChainId + ); + if (peopleApi) { - ActivePoolsController.syncPools( - api, - peopleApi, - peopleApiStatus, - activeAccount, - newActivePools - ); + ActivePoolsController.syncPools(network, activeAccount, newActivePool); } } else { // No active pools to sync. Mark as complete. @@ -118,7 +98,7 @@ export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { const assignActivePoolId = () => { // Membership takes priority, followed by the first pool the account has a role in. Falls back // to `null` if no active roles are found. - const initialActivePoolId = membership?.poolId || accountPoolIds[0] || null; + const initialActivePoolId = membership?.poolId || null; if (initialActivePoolId && !activePool) { setActivePoolId(String(initialActivePoolId)); } @@ -213,10 +193,13 @@ export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { // Fetch and update unclaimed pool rewards for an address from runtime call. const fetchPendingRewards = async (address: string | undefined) => { - if (api && address) { - const pendingRewards = - await api.call.nominationPoolsApi.pendingRewards(address); - return new BigNumber(pendingRewards?.toString() || 0); + const { pApi } = ApiController.get(network); + if (pApi && address) { + const apiResult = await pApi.apis.NominationPoolsApi.pending_rewards( + address, + { at: 'best' } + ); + return new BigNumber(apiResult?.toString() || 0); } return new BigNumber(0); }; @@ -234,7 +217,7 @@ export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { syncActivePoolSubscriptions(); assignActivePoolId(); } - }, [network, isReady, accountPoolIds]); + }, [network, isReady, membership]); // Reset on network change and component unmount. NOTE: ActivePoolsController also unsubscribes on // network change; this is handled by the Api instance. diff --git a/packages/app/src/contexts/Pools/BondedPools/defaults.ts b/packages/app/src/contexts/Pools/BondedPools/defaults.ts index 693148afc6..7209101b02 100644 --- a/packages/app/src/contexts/Pools/BondedPools/defaults.ts +++ b/packages/app/src/contexts/Pools/BondedPools/defaults.ts @@ -12,7 +12,6 @@ export const defaultBondedPoolsContext: BondedPoolsContextState = { removeFromBondedPools: (poolId) => {}, getPoolNominationStatus: (nominator, address) => {}, getPoolNominationStatusCode: (statuses) => '', - getAccountPoolRoles: (address) => null, replacePoolRoles: (poolId, roleEdits) => {}, poolSearchFilter: (filteredPools, searchTerm) => [], bondedPools: [], diff --git a/packages/app/src/contexts/Pools/BondedPools/index.tsx b/packages/app/src/contexts/Pools/BondedPools/index.tsx index f4fa5190fa..2aced342df 100644 --- a/packages/app/src/contexts/Pools/BondedPools/index.tsx +++ b/packages/app/src/contexts/Pools/BondedPools/index.tsx @@ -4,18 +4,21 @@ import { u8aToString, u8aUnwrapBytes } from '@polkadot/util'; import { useEffectIgnoreInitial } from '@w3ux/hooks'; import type { AnyJson, Sync } from '@w3ux/types'; -import { rmCommas, setStateWithRef, shuffle } from '@w3ux/utils'; +import { setStateWithRef, shuffle } from '@w3ux/utils'; import { useNetwork } from 'contexts/Network'; import { useStaking } from 'contexts/Staking'; +import { ApiController } from 'controllers/Api'; import { SyncController } from 'controllers/Sync'; import { useCreatePoolAccounts } from 'hooks/useCreatePoolAccounts'; +import { BondedPools } from 'model/Entries/BondedPools'; +import { NominatorsMulti } from 'model/Query/NominatorsMulti'; +import { PoolMetadataMulti } from 'model/Query/PoolMetadataMulti'; import type { ReactNode } from 'react'; import { createContext, useContext, useRef, useState } from 'react'; import type { AnyApi, MaybeAddress } from 'types'; import { useApi } from '../../Api'; import { defaultBondedPoolsContext } from './defaults'; import type { - AccountPoolRoles, BondedPool, BondedPoolsContextState, MaybePool, @@ -33,7 +36,6 @@ export const useBondedPools = () => useContext(BondedPoolsContext); export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { const { network } = useNetwork(); const { - api, isReady, activeEra, poolsConfig: { lastPoolId }, @@ -63,31 +65,32 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { // Fetch all bonded pool entries and their metadata. const fetchBondedPools = async () => { - if (!api || bondedPoolsSynced.current !== 'unsynced') { + const { pApi } = ApiController.get(network); + + if (!pApi || bondedPoolsSynced.current !== 'unsynced') { return; } bondedPoolsSynced.current = 'syncing'; + // Get and format bonded pool entries. const ids: number[] = []; + const idsMulti: [number][] = []; + const bondedPoolsEntries = (await new BondedPools(pApi).fetch()).format(); - // Fetch bonded pools entries. - const bondedPoolsMulti = - await api.query.nominationPools.bondedPools.entries(); - let exposures = bondedPoolsMulti.map(([keys, val]: AnyApi) => { - const id = keys.toHuman()[0]; - ids.push(id); - return getPoolWithAddresses(id, val.toHuman()); - }); + const exposures = shuffle( + Object.entries(bondedPoolsEntries).map(([id, pool]: AnyApi) => { + ids.push(id); + idsMulti.push([id]); + return getPoolWithAddresses(id, pool); + }) + ); - exposures = shuffle(exposures); setStateWithRef(exposures, setBondedPools, bondedPoolsRef); // Fetch pools metadata. - const metadataMulti = await api.query.nominationPools.metadata.multi(ids); + const metadataQuery = await new PoolMetadataMulti(pApi, idsMulti).fetch(); setPoolsMetadata( - Object.fromEntries( - metadataMulti.map((m, i) => [ids[i], String(m.toHuman())]) - ) + Object.fromEntries(metadataQuery.map((m, i) => [ids[i], m])) ); bondedPoolsSynced.current = 'synced'; @@ -96,47 +99,35 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { // Fetches pool nominations and updates state. const fetchPoolsNominations = async () => { - if (!api) { + const { pApi } = ApiController.get(network); + if (!pApi) { return; } const ids: number[] = []; - const nominationsMulti = await api.query.staking.nominators.multi( - bondedPools.map(({ addresses, id }) => { - ids.push(id); - return addresses.stash; - }) - ); + const stashes: [string][] = bondedPools.map(({ addresses, id }) => { + ids.push(id); + return [addresses.stash]; + }); + const nominationsMulti = await new NominatorsMulti(pApi, stashes).fetch(); setPoolsNominations(formatPoolsNominations(nominationsMulti, ids)); }; // Format raw pool nominations data. const formatPoolsNominations = (raw: AnyJson, ids: number[]) => Object.fromEntries( - raw.map((n: AnyJson, i: number) => { - const human = n.toHuman() as PoolNominations; - if (!human) { + raw.map((nominator: AnyJson, i: number) => { + if (!nominator) { return [ids[i], null]; } - return [ - ids[i], - { - ...human, - submittedIn: rmCommas(human.submittedIn), - }, - ]; + return [ids[i], { ...nominator }]; }) ); // Queries a bonded pool and injects ID and addresses to a result. const queryBondedPool = async (id: number) => { - if (!api) { - return null; - } - - const bondedPool: AnyApi = ( - await api.query.nominationPools.bondedPools(id) - ).toHuman(); + const { pApi } = ApiController.get(network); + const bondedPool = new BondedPools(pApi).fetchOne(id); if (!bondedPool) { return null; @@ -292,68 +283,6 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { } }; - // Gets all pools that the account has a role in. Returns an object with each pool role as keys, - // and array of pool ids as their values. - const accumulateAccountPoolRoles = (who: MaybeAddress): AccountPoolRoles => { - if (!who) { - return { - root: [], - depositor: [], - nominator: [], - bouncer: [], - }; - } - - const depositor = bondedPoolsRef.current - .filter((b) => b.roles.depositor === who) - .map((b) => b.id); - - const root = bondedPoolsRef.current - .filter((b: BondedPool) => b.roles.root === who) - .map((b) => b.id); - - const nominator = bondedPoolsRef.current - .filter((b) => b.roles.nominator === who) - .map((b) => b.id); - - const bouncer = bondedPoolsRef.current - .filter((b) => b.roles.bouncer === who) - .map((b) => b.id); - - const result = { - root, - depositor, - nominator, - bouncer, - }; - - return result; - }; - - // Gets a list of roles for all the pools the provided account has one or more roles in. - const getAccountPoolRoles = (who: MaybeAddress) => { - const allAccountRoles = accumulateAccountPoolRoles(who); - - // Reformat all roles object, keyed by pool id. - const pools: Record = {}; - - if (allAccountRoles) { - Object.entries(allAccountRoles).forEach(([role, poolIds]) => { - poolIds.forEach((poolId) => { - const exists = Object.keys(pools).find( - (k) => String(k) === String(poolId) - ); - if (!exists) { - pools[poolId] = [role]; - } else { - pools[poolId].push(role); - } - }); - }); - } - return pools; - }; - // Determine roles to replace from roleEdits const toReplace = (roleEdits: AnyJson) => { const root = roleEdits?.root?.newAddress ?? ''; @@ -423,7 +352,6 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { removeFromBondedPools, getPoolNominationStatus, getPoolNominationStatusCode, - getAccountPoolRoles, replacePoolRoles, poolSearchFilter, bondedPools, diff --git a/packages/app/src/contexts/Pools/BondedPools/types.ts b/packages/app/src/contexts/Pools/BondedPools/types.ts index b16a5fef4f..cdfee05dcc 100644 --- a/packages/app/src/contexts/Pools/BondedPools/types.ts +++ b/packages/app/src/contexts/Pools/BondedPools/types.ts @@ -18,7 +18,6 @@ export interface BondedPoolsContextState { address: MaybeAddress ) => AnyApi; getPoolNominationStatusCode: (statuses: NominationStatuses | null) => string; - getAccountPoolRoles: (address: MaybeAddress) => AnyApi; replacePoolRoles: (poolId: number, roleEdits: AnyJson) => void; poolSearchFilter: (filteredPools: AnyFilter, searchTerm: string) => AnyJson[]; bondedPools: BondedPool[]; diff --git a/packages/app/src/contexts/Pools/JoinPools/index.tsx b/packages/app/src/contexts/Pools/JoinPools/index.tsx index bd991a3b93..ad82e8bbff 100644 --- a/packages/app/src/contexts/Pools/JoinPools/index.tsx +++ b/packages/app/src/contexts/Pools/JoinPools/index.tsx @@ -22,7 +22,7 @@ export const useJoinPools = () => useContext(JoinPoolsContext); export const JoinPoolsProvider = ({ children }: { children: ReactNode }) => { const { - api, + isReady, activeEra, networkMetrics: { minimumActiveStake }, } = useApi(); @@ -45,7 +45,7 @@ export const JoinPoolsProvider = ({ children }: { children: ReactNode }) => { // Trigger worker to calculate join pool performance data. useEffectIgnoreInitial(() => { if ( - api && + isReady && bondedPools.length && activeEra.index.isGreaterThan(0) && erasRewardPointsFetched === 'synced' && diff --git a/packages/app/src/contexts/Pools/PoolMembers/defaults.ts b/packages/app/src/contexts/Pools/PoolMembers/defaults.ts index 80e09fc700..c54781631a 100644 --- a/packages/app/src/contexts/Pools/PoolMembers/defaults.ts +++ b/packages/app/src/contexts/Pools/PoolMembers/defaults.ts @@ -6,13 +6,9 @@ import type { PoolMemberContext } from './types'; export const defaultPoolMembers: PoolMemberContext = { fetchPoolMembersMetaBatch: (k, v, r) => {}, - queryPoolMember: (who) => new Promise((resolve) => resolve(null)), - getMembersOfPoolFromNode: (poolId) => null, - addToPoolMembers: (m) => {}, removePoolMember: (w) => {}, poolMembersApi: [], setPoolMembersApi: (p) => {}, - poolMembersNode: [], meta: {}, fetchedPoolMembersApi: 'unsynced', setFetchedPoolMembersApi: (s) => {}, diff --git a/packages/app/src/contexts/Pools/PoolMembers/index.tsx b/packages/app/src/contexts/Pools/PoolMembers/index.tsx index 2fb1c8866d..4acbf0bf3a 100644 --- a/packages/app/src/contexts/Pools/PoolMembers/index.tsx +++ b/packages/app/src/contexts/Pools/PoolMembers/index.tsx @@ -7,9 +7,13 @@ import { setStateWithRef } from '@w3ux/utils'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useNetwork } from 'contexts/Network'; import { usePlugins } from 'contexts/Plugins'; +import { SubscriptionsController } from 'controllers/Subscriptions'; +import { isCustomEvent } from 'controllers/utils'; +import { PoolMembersMulti } from 'model/Subscribe/PoolMembersMulti'; import type { ReactNode } from 'react'; import { createContext, useContext, useRef, useState } from 'react'; -import type { AnyApi, AnyMetaBatch, Fn, MaybeAddress } from 'types'; +import type { AnyMetaBatch, MaybeAddress } from 'types'; +import { useEventListener } from 'usehooks-ts'; import { useApi } from '../../Api'; import { defaultPoolMembers } from './defaults'; import type { PoolMember, PoolMemberContext } from './types'; @@ -20,14 +24,11 @@ export const PoolMembersContext = export const usePoolMembers = () => useContext(PoolMembersContext); export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { + const { isReady } = useApi(); const { network } = useNetwork(); - const { api, isReady } = useApi(); const { pluginEnabled } = usePlugins(); const { activeAccount } = useActiveAccounts(); - // Store pool members from node. - const [poolMembersNode, setPoolMembersNode] = useState([]); - // Store pool members from api. const [poolMembersApi, setPoolMembersApi] = useState([]); @@ -39,8 +40,8 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { useState({}); const poolMembersMetaBatchesRef = useRef(poolMembersMetaBatches); - // Stores the meta batch subscriptions for pool lists. - const poolMembersSubs = useRef>({}); + // Stores the meta batch subscription keys for pool lists. + const poolMembersSubs = useRef([]); // Update poolMembersApi fetched status. const setFetchedPoolMembersApi = (status: Sync) => { @@ -49,7 +50,6 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { // Clear existing state for network refresh useEffectIgnoreInitial(() => { - setPoolMembersNode([]); setPoolMembersApi([]); unsubscribeAndResetMeta(); }, [network]); @@ -62,12 +62,7 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { // Initial setup for fetching members if Subscan is not enabled. Ensure poolMembers are reset if // subscan is disabled. useEffectIgnoreInitial(() => { - if (!pluginEnabled('subscan')) { - if (isReady) { - fetchPoolMembersNode(); - } - } else { - setPoolMembersNode([]); + if (pluginEnabled('subscan')) { setPoolMembersApi([]); } return () => { @@ -77,77 +72,22 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { const unsubscribe = () => { unsubscribeAndResetMeta(); - setPoolMembersNode([]); setPoolMembersApi([]); }; const unsubscribeAndResetMeta = () => { - Object.values(poolMembersSubs.current).map((batch: Fn[]) => - Object.entries(batch).map(([, v]) => v()) + Object.values(poolMembersSubs.current).map((key: string) => + SubscriptionsController.remove(network, `poolMembersBatch-${key}`) ); setStateWithRef({}, setPoolMembersMetaBatch, poolMembersMetaBatchesRef); }; - // Fetch all pool members entries from node. - const fetchPoolMembersNode = async () => { - if (!api) { - return; - } - const result = await api.query.nominationPools.poolMembers.entries(); - const newMembers = result.map(([keys, val]: AnyApi) => { - const who = keys.toHuman()[0]; - const { poolId } = val.toHuman(); - return { - who, - poolId, - }; - }); - setPoolMembersNode(newMembers); - }; - - const getMembersOfPoolFromNode = (poolId: number) => - poolMembersNode.filter((p) => String(p.poolId) === String(poolId)) ?? null; - - // queries a pool member and formats to `PoolMember`. - const queryPoolMember = async (who: MaybeAddress) => { - if (!api) { - return null; - } - - const poolMember: AnyApi = ( - await api.query.nominationPools.poolMembers(who) - ).toHuman(); - - if (!poolMember) { - return null; - } - - return { - who, - poolId: poolMember.poolId, - } as PoolMember; - }; - - /* - Fetches a new batch of pool member metadata. - structure: - { - key: { - [ - { - identities: [], - super_identities: [], - } - ] - }, - }; - */ const fetchPoolMembersMetaBatch = async ( key: string, p: AnyMetaBatch, refetch = false ) => { - if (!isReady || !api) { + if (!isReady) { return; } if (!p.length) { @@ -170,15 +110,16 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { poolMembersMetaBatchesRef ); - if (poolMembersSubs.current[key] !== undefined) { - for (const unsub of poolMembersSubs.current[key]) { - unsub(); - } + if (key in poolMembersSubs.current) { + SubscriptionsController.remove(network, `poolMembersBatch-${key}`); + poolMembersSubs.current = poolMembersSubs.current.filter( + (item) => item !== key + ); } } // aggregate member addresses - const addresses = []; + const addresses: string[] = []; for (const { who } of p) { addresses.push(who); } @@ -193,77 +134,51 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { poolMembersMetaBatchesRef ); - const subscribeToPoolMembers = async (addr: string[]) => { - const unsub = await api.query.nominationPools.poolMembers.multi( - addr, - (_pools) => { - const pools = []; - for (const _pool of _pools) { - pools.push(_pool.toHuman()); - } - const updated = Object.assign(poolMembersMetaBatchesRef.current); - updated[key].poolMembers = pools; - setStateWithRef( - { ...updated }, - setPoolMembersMetaBatch, - poolMembersMetaBatchesRef - ); - } - ); - return unsub; - }; - - // initiate subscriptions - await Promise.all([subscribeToPoolMembers(addresses)]).then( - (unsubs: Fn[]) => { - addMetaBatchUnsubs(key, unsubs); - } + // initialise subscription + SubscriptionsController.set( + network, + `poolMembersBatch-${key}`, + new PoolMembersMulti(network, key, addresses) ); + + // Record key. + poolMembersSubs.current.push(key); }; - // Removes a member from the member list and updates state. + // Removes a member from the member list and updates state. Requires subscan to be enabled. const removePoolMember = (who: MaybeAddress) => { - // If Subscan is enabled, update API state, otherwise, update node state. if (pluginEnabled('subscan')) { setPoolMembersApi(poolMembersApi.filter((p) => p.who !== who) ?? []); - } else { - setPoolMembersNode(poolMembersNode.filter((p) => p.who !== who) ?? []); } }; - // Adds a record to poolMembers. - // Currently only used when an account joins or creates a pool. - const addToPoolMembers = (member: { who: string; poolId: number }) => { - if (!member || pluginEnabled('subscan')) { - return; - } + // Handle new pool members batch event. + const handleNewPoolMembersBatch = (e: Event) => { + if (isCustomEvent(e)) { + const { key, poolMembers } = e.detail; - const exists = poolMembersNode.find((m) => m.who === member.who); - if (!exists) { - setPoolMembersNode(poolMembersNode.concat(member)); + const updated = Object.assign(poolMembersMetaBatchesRef.current); + updated[key].poolMembers = poolMembers; + setStateWithRef( + { ...updated }, + setPoolMembersMetaBatch, + poolMembersMetaBatchesRef + ); } }; - /* - * Helper: to add mataBatch unsubs by key. - */ - const addMetaBatchUnsubs = (key: string, unsubs: Fn[]) => { - const subs = poolMembersSubs.current; - const sub = subs[key] ?? []; - sub.push(...unsubs); - subs[key] = sub; - poolMembersSubs.current = subs; - }; + const documentRef = useRef(document); + useEventListener( + 'new-pool-members-batch', + handleNewPoolMembersBatch, + documentRef + ); return ( void; - queryPoolMember: (who: MaybeAddress) => Promise; - getMembersOfPoolFromNode: (poolId: number) => PoolMember[] | null; - addToPoolMembers: (member: PoolMember) => void; removePoolMember: (w: MaybeAddress) => void; - poolMembersNode: PoolMember[]; meta: AnyMetaBatch; poolMembersApi: PoolMember[]; setPoolMembersApi: (p: PoolMember[]) => void; diff --git a/packages/app/src/contexts/Pools/PoolPerformance/index.tsx b/packages/app/src/contexts/Pools/PoolPerformance/index.tsx index 4546527dad..a8d9ced61c 100644 --- a/packages/app/src/contexts/Pools/PoolPerformance/index.tsx +++ b/packages/app/src/contexts/Pools/PoolPerformance/index.tsx @@ -38,7 +38,7 @@ export const PoolPerformanceProvider = ({ children: ReactNode; }) => { const { network } = useNetwork(); - const { api, activeEra } = useApi(); + const { isReady, activeEra } = useApi(); const { getPagedErasStakers } = useStaking(); const { erasRewardPoints } = useValidators(); @@ -179,10 +179,9 @@ export const PoolPerformanceProvider = ({ // Get era data and send to worker. const processEra = async (key: PoolRewardPointsKey, era: BigNumber) => { - if (!api) { + if (!isReady) { return; } - // NOTE: This will not make any difference on the first run. updateTaskCurrentEra(key, era); diff --git a/packages/app/src/contexts/Prompt/defaults.tsx b/packages/app/src/contexts/Prompt/defaults.tsx index d5bf4b736e..6328ffdfcf 100644 --- a/packages/app/src/contexts/Prompt/defaults.tsx +++ b/packages/app/src/contexts/Prompt/defaults.tsx @@ -6,11 +6,13 @@ import type { PromptContextInterface } from './types'; export const defaultPromptContext: PromptContextInterface = { setOnClosePrompt: (value) => {}, - openPromptWith: (o, s) => {}, + openPromptWith: (o, s, c) => {}, closePrompt: () => {}, setStatus: (s) => {}, setPrompt: (d) => {}, + closeOnOutsideClick: true, size: 'small', status: 0, Prompt: null, + setCloseOnOutsideClick: (canClose) => {}, }; diff --git a/packages/app/src/contexts/Prompt/index.tsx b/packages/app/src/contexts/Prompt/index.tsx index a9ac25a532..7a2e2dee93 100644 --- a/packages/app/src/contexts/Prompt/index.tsx +++ b/packages/app/src/contexts/Prompt/index.tsx @@ -19,6 +19,9 @@ export const PromptProvider = ({ children }: { children: ReactNode }) => { onClosePrompt: null, }); + // Whether prompt can be closed by clicking outside on container. + const [closeOnOutsideClick, setCloseOnOutsideClick] = useState(false); + const setPrompt = (Prompt: Prompt) => { setState({ ...state, @@ -33,13 +36,18 @@ export const PromptProvider = ({ children }: { children: ReactNode }) => { }); }; - const openPromptWith = (Prompt: Prompt, size = 'small') => { + const openPromptWith = ( + Prompt: Prompt, + size = 'small', + closeOutside = true + ) => { setState({ ...state, size, Prompt, status: 1, }); + setCloseOnOutsideClick(closeOutside); }; const closePrompt = () => { @@ -70,9 +78,11 @@ export const PromptProvider = ({ children }: { children: ReactNode }) => { closePrompt, setStatus, setPrompt, + setCloseOnOutsideClick, size: state.size, status: state.status, Prompt: state.Prompt, + closeOnOutsideClick, }} > {children} diff --git a/packages/app/src/contexts/Prompt/types.ts b/packages/app/src/contexts/Prompt/types.ts index 05869612cc..c6b4f797de 100644 --- a/packages/app/src/contexts/Prompt/types.ts +++ b/packages/app/src/contexts/Prompt/types.ts @@ -6,13 +6,19 @@ import type { MaybeString } from 'types'; export interface PromptContextInterface { setOnClosePrompt: (onClosePrompt: (() => void) | null) => void; - openPromptWith: (o: ReactNode | null, s?: string) => void; + openPromptWith: ( + o: ReactNode | null, + s?: string, + closeOnOutsideClick?: boolean + ) => void; closePrompt: () => void; setStatus: (s: number) => void; setPrompt: (d: MaybeString) => void; size: string; status: number; Prompt: Prompt; + closeOnOutsideClick: boolean; + setCloseOnOutsideClick: (canClose: boolean) => void; } export interface PromptState { diff --git a/packages/app/src/contexts/Proxies/index.tsx b/packages/app/src/contexts/Proxies/index.tsx index 28c69d8ae9..22c459f2d4 100644 --- a/packages/app/src/contexts/Proxies/index.tsx +++ b/packages/app/src/contexts/Proxies/index.tsx @@ -1,7 +1,6 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { VoidFn } from '@polkadot/api/types'; import { useEffectIgnoreInitial } from '@w3ux/hooks'; import { addedTo, @@ -9,7 +8,6 @@ import { localStorageOrDefault, matchedProperties, removedFrom, - rmCommas, setStateWithRef, } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; @@ -21,9 +19,15 @@ import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { useOtherAccounts } from 'contexts/Connect/OtherAccounts'; import { useNetwork } from 'contexts/Network'; import { defaultNetwork } from 'contexts/Network/defaults'; +import { ApiController } from 'controllers/Api'; +import { SubscriptionsController } from 'controllers/Subscriptions'; +import { isCustomEvent } from 'controllers/utils'; +import { ProxiesQuery } from 'model/Query/ProxiesQuery'; +import { AccountProxies } from 'model/Subscribe/AccountProxies'; import type { ReactNode } from 'react'; import { createContext, useContext, useRef, useState } from 'react'; import type { AnyApi, MaybeAddress, NetworkName } from 'types'; +import { useEventListener } from 'usehooks-ts'; import * as defaults from './defaults'; import type { Delegates, @@ -41,8 +45,8 @@ export const ProxiesContext = createContext( export const useProxies = () => useContext(ProxiesContext); export const ProxiesProvider = ({ children }: { children: ReactNode }) => { + const { isReady } = useApi(); const { network } = useNetwork(); - const { api, isReady } = useApi(); const { accounts } = useImportedAccounts(); const { addExternalAccount } = useExternalAccounts(); const { addOrReplaceOtherAccount } = useOtherAccounts(); @@ -51,7 +55,6 @@ export const ProxiesProvider = ({ children }: { children: ReactNode }) => { // Store the proxy accounts of each imported account. const [proxies, setProxies] = useState([]); const proxiesRef = useRef(proxies); - const unsubs = useRef>({}); // Store the last network proxies were synced on. const [lastSyncedNetwork, setLastSyncedNetwork] = @@ -106,17 +109,11 @@ export const ProxiesProvider = ({ children }: { children: ReactNode }) => { addOrReplaceOtherAccount(importResult.account, importResult.type); } } else { - const unsub = unsubs.current[address]; - if (unsub) { - unsub(); - } + SubscriptionsController.remove(network, `accountProxies-${address}`); } }); - - unsubs.current = Object.fromEntries( - Object.entries(unsubs.current).filter(([key]) => !removed.includes(key)) - ); }; + // Sync added accounts. const handleAddedAccounts = () => { addedTo(accounts, proxies, ['address'])?.map(({ address }) => @@ -144,48 +141,11 @@ export const ProxiesProvider = ({ children }: { children: ReactNode }) => { }; const subscribeToProxies = async (address: string) => { - if (!api) { - return undefined; - } - - const unsub = await api.queryMulti( - [[api.query.proxy.proxies, address]], - async ([result]) => { - const data = result.toHuman(); - const newProxies = data[0]; - const reserved = new BigNumber(rmCommas(data[1])); - - if (newProxies.length) { - setStateWithRef( - [...proxiesRef.current] - .filter(({ delegator }) => delegator !== address) - .concat({ - address, - delegator: address, - delegates: newProxies.map((d: AnyApi) => ({ - delegate: d.delegate.toString(), - proxyType: d.proxyType.toString(), - })), - reserved, - }), - setProxies, - proxiesRef - ); - } else { - // no proxies: remove stale proxies if already in list. - setStateWithRef( - [...proxiesRef.current].filter( - ({ delegator }) => delegator !== address - ), - setProxies, - proxiesRef - ); - } - } + SubscriptionsController.set( + network, + `accountProxies-${address}`, + new AccountProxies(network, address) ); - - unsubs.current[address] = unsub; - return unsub; }; // Gets the delegates of the given account. @@ -211,14 +171,16 @@ export const ProxiesProvider = ({ children }: { children: ReactNode }) => { // Queries the chain to check if the given delegator & delegate pair is valid proxy. Used when a // proxy account is being manually declared. const handleDeclareDelegate = async (delegator: string) => { - if (!api) { + const { pApi } = ApiController.get(network); + if (!pApi) { return []; } - const result: AnyApi = (await api.query.proxy.proxies(delegator)).toHuman(); + const result = await new ProxiesQuery(pApi, delegator).fetch(); + const proxy = result[0] || []; let addDelegatorAsExternal = false; - for (const { delegate: newDelegate } of result[0] || []) { + for (const { delegate: newDelegate } of proxy) { if ( accounts.find(({ address }) => address === newDelegate) && !delegates[newDelegate] @@ -246,6 +208,43 @@ export const ProxiesProvider = ({ children }: { children: ReactNode }) => { .find((p) => p.delegator === delegator) ?.delegates.find((d) => d.delegate === delegate) ?? null; + // Handle account proxies events. + const handleAccountProxies = (e: Event) => { + if (isCustomEvent(e)) { + const { address: eventAddress, proxies: eventProxies } = e.detail; + + const newProxies = eventProxies[0]; + const reserved = new BigNumber(eventProxies[1].toString()); + + if (newProxies.length) { + setStateWithRef( + [...proxiesRef.current] + .filter(({ delegator }) => delegator !== eventAddress) + .concat({ + address: eventAddress, + delegator: eventAddress, + delegates: newProxies.map((d: AnyApi) => ({ + delegate: d.delegate.toString(), + proxyType: d.proxy_type.type.toString(), + })), + reserved, + }), + setProxies, + proxiesRef + ); + } else { + // no proxies: remove stale proxies if already in list. + setStateWithRef( + [...proxiesRef.current].filter( + ({ delegator }) => delegator !== eventAddress + ), + setProxies, + proxiesRef + ); + } + } + }; + // If active proxy has not yet been set, check local storage `activeProxy` & set it as active // proxy if it is the delegate of `activeAccount`. useEffectIgnoreInitial(() => { @@ -293,20 +292,14 @@ export const ProxiesProvider = ({ children }: { children: ReactNode }) => { } }, [accounts, isReady]); - // Reset active proxy state, unsubscribe from subscriptions on network change & unmount. + // Reset active proxy state on network change & unmount. useEffectIgnoreInitial(() => { setStateWithRef([], setProxies, proxiesRef); setActiveProxy(null, false); - unsubAll(); - return () => unsubAll(); }, [network]); - const unsubAll = () => { - for (const unsub of Object.values(unsubs.current)) { - unsub(); - } - unsubs.current = {}; - }; + const documentRef = useRef(document); + useEventListener('new-account-proxies', handleAccountProxies, documentRef); return ( { const { getBondedAccount } = useBonded(); const { networkData, network } = useNetwork(); const { getLedger, getNominations } = useBalances(); + const { isReady, activeEra, apiStatus } = useApi(); const { accounts: connectAccounts } = useImportedAccounts(); const { activeAccount, getActiveAccount } = useActiveAccounts(); - const { isReady, api, apiStatus, activeEra } = useApi(); // Store eras stakers in state. const [eraStakers, setEraStakers] = useState(defaultEraStakers); @@ -90,7 +93,7 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { // Fetches erasStakers exposures for an era, and saves to `localStorage`. const fetchEraStakers = async (era: string) => { - if (!isReady || activeEra.index.isZero() || !api) { + if (!isReady || activeEra.index.isZero()) { return []; } @@ -117,7 +120,7 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { // Fetches the active nominator set and metadata around it. const fetchActiveEraStakers = async () => { - if (!isReady || activeEra.index.isZero() || !api) { + if (!isReady || activeEra.index.isZero()) { return; } @@ -221,54 +224,55 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { // Fetch eras stakers from storage. const getPagedErasStakers = async (era: string) => { - if (!api) { + const { pApi } = ApiController.get(network); + if (!pApi) { return []; } - const overview: AnyApi = - await api.query.staking.erasStakersOverview.entries(era); - - const validators = overview.reduce( - (prev: Record, [keys, value]: AnyApi) => { - const validator = keys.toHuman()[1]; - const { own, total } = value.toHuman(); - return { ...prev, [validator]: { own, total } }; - }, + const overview = await new ErasStakersOverview(pApi).fetch(Number(era)); + const validators: Record = overview.reduce( + ( + prev: Record, + { keyArgs: [, validator], value: { own, total } }: AnyApi + ) => ({ ...prev, [validator]: { own, total } }), {} ); const validatorKeys = Object.keys(validators); const pagedResults = await Promise.all( - validatorKeys.map((v) => - api.query.staking.erasStakersPaged.entries(era, v) - ) + validatorKeys.map((v) => new ErasStakersPaged(pApi).fetch(Number(era), v)) ); const result: Exposure[] = []; let i = 0; - for (const pagedResult of pagedResults) { + for (const pages of pagedResults) { + // NOTE: Only one page is fetched for each validator for now. + const page = pages[0]; + + // NOTE: Some pages turn up as undefined - might be worth exploring further. + if (!page) { + continue; + } + + const { + keyArgs, + value: { others }, + } = page; + const validator = validatorKeys[i]; const { own, total } = validators[validator]; - const others = pagedResult.reduce( - (prev: ExposureOther[], [, v]: AnyApi) => { - const o = v.toHuman()?.others || []; - if (!o.length) { - return prev; - } - return prev.concat(o); - }, - [] - ); result.push({ - keys: [rmCommas(era), validator], + keys: [keyArgs[0].toString(), validator], val: { - total: rmCommas(total), - own: rmCommas(own), - others: others.map(({ who, value }) => ({ - who, - value: rmCommas(value), - })), + total: total.toString(), + own: own.toString(), + others: others.map( + ({ who, value }: { who: string; value: bigint }) => ({ + who, + value: value.toString(), + }) + ), }, }); i++; diff --git a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx index 7bdf738095..e51c40d5e8 100644 --- a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx +++ b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx @@ -3,17 +3,25 @@ import { useEffectIgnoreInitial } from '@w3ux/hooks'; import type { AnyJson, Sync } from '@w3ux/types'; -import { rmCommas, shuffle } from '@w3ux/utils'; +import { shuffle } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import { MaxEraRewardPointsEras } from 'consts'; import { useApi } from 'contexts/Api'; import { useNetwork } from 'contexts/Network'; import { useStaking } from 'contexts/Staking'; +import { ApiController } from 'controllers/Api'; import { IdentitiesController } from 'controllers/Identities'; import { useErasPerDay } from 'hooks/useErasPerDay'; +import { perbillToPercent } from 'library/Utils'; +import { Validators } from 'model/Entries/Validators'; +import { ErasRewardPointsMulti } from 'model/Query/ErasRewardPointsMulti'; +import { ErasValidatorReward } from 'model/Query/ErasValidatorRewardMulti'; +import { ParaSessionAccounts } from 'model/Query/ParaSessionAccounts'; +import { SessionValidators } from 'model/Query/SessionValidators'; +import { ValidatorsMulti } from 'model/Query/ValidatorsMulti'; import type { ReactNode } from 'react'; -import { createContext, useContext, useEffect, useRef, useState } from 'react'; -import type { AnyApi, Fn } from 'types'; +import { createContext, useContext, useEffect, useState } from 'react'; +import type { AnyApi, SystemChainId } from 'types'; import type { EraPointsBoundaries, ErasRewardPoints, @@ -44,8 +52,6 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { const { network } = useNetwork(); const { isReady, - api, - peopleApi, peopleApiStatus, consts: { historyDepth }, networkMetrics: { earliestStoredSession }, @@ -78,9 +84,6 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { [] ); - // Stores unsub object for para session. - const sessionParaUnsub = useRef(); - // Stores the average network commission rate. const [avgCommission, setAvgCommission] = useState(0); @@ -115,11 +118,11 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { } return { - total: rmCommas(result.total), + total: result.total.toString(), individual: Object.fromEntries( - Object.entries(result.individual).map(([key, value]) => [ + result.individual.map(([key, value]: [number, string]) => [ key, - rmCommas(value as string), + (value as string).toString(), ]) ), }; @@ -145,9 +148,10 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { // Fetches era reward points for eligible eras. const fetchErasRewardPoints = async () => { + const { pApi } = ApiController.get(network); if ( activeEra.index.isZero() || - !api || + !pApi || erasRewardPointsFetched !== 'unsynced' ) { return; @@ -167,12 +171,9 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { let erasProcessed = new BigNumber(0); // Iterate eras and process reward points. - const calls = []; const eras = []; do { - calls.push(api.query.staking.erasRewardPoints(currentEra.toString())); eras.push(currentEra); - currentEra = currentEra.minus(1); erasProcessed = erasProcessed.plus(1); } while ( @@ -180,11 +181,14 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { erasProcessed.isLessThan(totalEras) ); + const erasMulti: [number][] = eras.map((e) => [e.toNumber()]); + const results = await new ErasRewardPointsMulti(pApi, erasMulti).fetch(); + // Make calls and format reward point results. const newErasRewardPoints: ErasRewardPoints = {}; let i = 0; - for (const result of await Promise.all(calls)) { - const formatted = processEraRewardPoints(result.toHuman(), eras[i]); + for (const result of results) { + const formatted = processEraRewardPoints(result, eras[i]); if (formatted) { newErasRewardPoints[eras[i].toString()] = formatted; } @@ -240,41 +244,43 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { // Fetch validator entries and format the returning data. const getValidatorEntries = async () => { - if (!isReady || !api) { + if (!isReady) { return defaultValidatorsData; } - const result = await api.query.staking.validators.entries(); + const { pApi } = ApiController.get(network); + const result = await new Validators(pApi).fetch(); const entries: Validator[] = []; let notFullCommissionCount = 0; let totalNonAllCommission = new BigNumber(0); - result.forEach(([a, p]: AnyApi) => { - const address = a.toHuman().pop(); - const prefs = p.toHuman(); - const commission = new BigNumber(prefs.commission.replace(/%/g, '')); + result.forEach( + ({ keyArgs: [address], value: { commission, blocked } }: AnyApi) => { + const commissionAsPercent = perbillToPercent(commission); - if (!commission.isEqualTo(100)) { - totalNonAllCommission = totalNonAllCommission.plus(commission); - } else { - notFullCommissionCount++; - } + if (!commissionAsPercent.isEqualTo(100)) { + totalNonAllCommission = + totalNonAllCommission.plus(commissionAsPercent); + } else { + notFullCommissionCount++; + } - entries.push({ - address, - prefs: { - commission: Number(commission.toFixed(2)), - blocked: prefs.blocked, - }, - }); - }); + entries.push({ + address, + prefs: { + commission: Number(commissionAsPercent.toFixed(2)), + blocked, + }, + }); + } + ); return { entries, notFullCommissionCount, totalNonAllCommission }; }; // Fetches and formats the active validator set, and derives metrics from the result. const fetchValidators = async () => { - if (!isReady || !api || validatorsFetched !== 'unsynced') { + if (!isReady || validatorsFetched !== 'unsynced') { return; } setValidatorsFetched('syncing'); @@ -317,7 +323,9 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { setAvgCommission(avg); // NOTE: validators are shuffled before committed to state. setValidators(shuffle(validatorEntries)); - + const { pApi: peopleApi } = ApiController.get( + `people-${network}` as SystemChainId + ); if (peopleApi && peopleApiStatus === 'ready') { const addresses = validatorEntries.map(({ address }) => address); const { identities, supers } = await IdentitiesController.fetch( @@ -333,49 +341,54 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { // Subscribe to active session validators. const fetchSessionValidators = async () => { - if (!api || !isReady) { + if (!isReady) { return; } - const sessionValidatorsRaw: AnyApi = await api.query.session.validators(); - setSessionValidators(sessionValidatorsRaw.toHuman()); + const { pApi } = ApiController.get(network); + setSessionValidators(await new SessionValidators(pApi).fetch()); }; // Subscribe to active parachain validators. - const subscribeParachainValidators = async () => { - if (!api || !isReady) { - return; - } - const unsub: AnyApi = await api.query.paraSessionInfo.accountKeys( - earliestStoredSession.toString(), - (v: AnyApi) => { - setSessionParaValidators(v.toHuman()); - sessionParaUnsub.current = unsub; - } + const getParachainValidators = async () => { + const { pApi } = ApiController.get(network); + setSessionParaValidators( + await new ParaSessionAccounts( + pApi, + earliestStoredSession.toNumber() + ).fetch() ); }; // Fetches prefs for a list of validators. const fetchValidatorPrefs = async (addresses: ValidatorAddresses) => { - if (!addresses.length || !api) { + if (!addresses.length) { return null; } const v: string[] = []; + const vMulti: [string][] = []; for (const { address } of addresses) { v.push(address); + vMulti.push([address]); } - const results = await api.query.staking.validators.multi(v); + + const { pApi } = ApiController.get(network); + const resultsMulti = + (await new ValidatorsMulti(pApi, vMulti).fetch()) || []; const formatted: Validator[] = []; - for (let i = 0; i < results.length; i++) { - const prefs: AnyApi = results[i].toHuman(); - formatted.push({ - address: v[i], - prefs: { - commission: prefs?.commission.replace(/%/g, '') ?? '0', - blocked: prefs.blocked, - }, - }); + for (let i = 0; i < resultsMulti.length; i++) { + const prefs: AnyApi = resultsMulti[i]; + + if (prefs) { + formatted.push({ + address: v[i], + prefs: { + commission: Number(perbillToPercent(prefs.commission).toString()), + blocked: prefs.blocked, + }, + }); + } } return formatted; }; @@ -473,7 +486,9 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { // Gets average validator reward for provided number of days. const getAverageEraValidatorReward = async () => { - if (!api || !isReady || activeEra.index.isZero()) { + const { pApi } = ApiController.get(network); + + if (!pApi || !isReady || activeEra.index.isZero()) { setAverageEraValidatorReward({ days: 0, reward: new BigNumber(0), @@ -498,12 +513,12 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { thisEra = thisEra.minus(1); } while (thisEra.gte(endEra)); - const validatorEraRewards = - await api.query.staking.erasValidatorReward.multi(eras); + const erasMulti: [number][] = eras.map((e) => [Number(e)]); + const results = await new ErasValidatorReward(pApi, erasMulti).fetch(); - const reward = validatorEraRewards + const reward = results .map((v) => { - const value = new BigNumber(v.toString() === '' ? 0 : v.toString()); + const value = new BigNumber(!v ? 0 : v.toString()); if (value.isNaN()) { return new BigNumber(0); } @@ -574,18 +589,10 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { // Fetch parachain session validators when `earliestStoredSession` ready. useEffectIgnoreInitial(() => { if (isReady && earliestStoredSession.isGreaterThan(0)) { - subscribeParachainValidators(); + getParachainValidators(); } }, [isReady, earliestStoredSession]); - // Unsubscribe on network change and component unmount. - useEffect(() => { - sessionParaUnsub.current?.(); - return () => { - sessionParaUnsub.current?.(); - }; - }, [network]); - return ( Promise.resolve(), wcSessionActive: false, fetchAddresses: () => Promise.resolve([]), - signWcTx: (caip, payload, from) => Promise.resolve(null), + signWcTx: (payload) => Promise.resolve({ signature: '0x' }), }; diff --git a/packages/app/src/contexts/WalletConnect/index.tsx b/packages/app/src/contexts/WalletConnect/index.tsx index 497c4df09b..e9f319f220 100644 --- a/packages/app/src/contexts/WalletConnect/index.tsx +++ b/packages/app/src/contexts/WalletConnect/index.tsx @@ -7,6 +7,7 @@ import UniversalProvider from '@walletconnect/universal-provider'; import { getSdkError } from '@walletconnect/utils'; import { useApi } from 'contexts/Api'; import { useNetwork } from 'contexts/Network'; +import { ApiController } from 'controllers/Api'; import { getUnixTime } from 'date-fns'; import type { ReactNode } from 'react'; import { createContext, useContext, useEffect, useRef, useState } from 'react'; @@ -27,10 +28,13 @@ export const WalletConnectProvider = ({ children: ReactNode; }) => { const { network } = useNetwork(); - const { isReady, api } = useApi(); + const { + isReady, + chainSpecs: { genesisHash }, + } = useApi(); // Check if the API is present. - const apiPresent = api !== null; + const apiPresent = !!ApiController.get(network); // The WalletConnect provider. const wcProvider = useRef(null); @@ -87,7 +91,7 @@ export const WalletConnectProvider = ({ // Connect WalletConnect provider and retrieve metadata. const connectProvider = async () => { - if (!wcInitialized || !api) { + if (!wcInitialized) { return; } @@ -99,9 +103,7 @@ export const WalletConnectProvider = ({ // Update most recent connected chain. sessionChain.current = network; - const caips = [ - `polkadot:${api.genesisHash.toHex().substring(2).substring(0, 32)}`, - ]; + const caips = [`polkadot:${genesisHash.substring(2).substring(0, 32)}`]; // If there are no chains connected, return early. if (!caips.length) { @@ -159,15 +161,12 @@ export const WalletConnectProvider = ({ // Update session namespaces. NOTE: This method is currently not in use due to a // default chain error upon reconnecting to the session. const updateWcSession = async () => { - if (!wcInitialized || !api) { + if (!wcInitialized) { return; } // Update most recent connected chains. sessionChain.current = network; - - const caips = [ - `polkadot:${api.genesisHash.toHex().substring(2).substring(0, 32)}`, - ]; + const caips = [`polkadot:${genesisHash.substring(2).substring(0, 32)}`]; // If there are no chains connected, return early. if (!caips.length) { @@ -252,36 +251,26 @@ export const WalletConnectProvider = ({ }; // Attempt to sign a transaction and receive a signature. - const signWcTx = async ( - caip: string, - payload: AnyJson, - from: string - ): Promise => { + const signWcTx = async (payload: AnyJson): Promise<{ signature: string }> => { if (!wcProvider.current || !wcProvider.current.session?.topic) { - return null; + return { signature: '0x' }; } const topic = wcProvider.current.session.topic; - - const result: { signature: string } = - await wcProvider.current.client.request({ - chainId: caip, - topic, - request: { - method: 'polkadot_signTransaction', - params: { - address: from, - transactionPayload: payload, - }, + const caip = `polkadot:${genesisHash.substring(2).substring(0, 32)}`; + return await wcProvider.current.client.request({ + chainId: caip, + topic, + request: { + method: 'polkadot_signTransaction', + params: { + address: payload.address, + transactionPayload: payload, }, - }); - - return result?.signature || null; + }, + }); }; const fetchAddresses = async (): Promise => { - if (!api) { - return []; - } // Retrieve a new session or get current one. const wcSession = await initializeWcSession(); if (wcSession === null) { @@ -293,7 +282,7 @@ export const WalletConnectProvider = ({ .map((namespace: AnyJson) => namespace.accounts) .flat(); - const caip = api.genesisHash.toHex().substring(2).substring(0, 32); + const caip = genesisHash.substring(2).substring(0, 32); // Only get accounts for the currently selected `caip`. let filteredAccounts = walletConnectAccounts.filter((wcAccount) => { diff --git a/packages/app/src/contexts/WalletConnect/types.ts b/packages/app/src/contexts/WalletConnect/types.ts index 99499995c1..77beb835c6 100644 --- a/packages/app/src/contexts/WalletConnect/types.ts +++ b/packages/app/src/contexts/WalletConnect/types.ts @@ -11,14 +11,14 @@ export interface WalletConnectContextInterface { updateWcSession: () => Promise; disconnectWcSession: () => Promise; fetchAddresses: () => Promise; - signWcTx: ( - caip: string, - payload: AnyJson, - from: string - ) => Promise; + signWcTx: WalletConnectSignTx; } export interface WalletConnectConnectedMeta { uri: string | undefined; approval: AnyFunction; } + +export type WalletConnectSignTx = ( + payload: AnyJson +) => Promise<{ signature: string }>; diff --git a/packages/app/src/controllers/ActivePools/index.ts b/packages/app/src/controllers/ActivePools/index.ts index fd74545028..3291414687 100644 --- a/packages/app/src/controllers/ActivePools/index.ts +++ b/packages/app/src/controllers/ActivePools/index.ts @@ -1,49 +1,28 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { ApiPromise } from '@polkadot/api'; -import type { VoidFn } from '@polkadot/api/types'; -import type { Nominations } from 'contexts/Balances/types'; import { defaultPoolNominations } from 'contexts/Pools/ActivePool/defaults'; -import type { ActivePool, PoolRoles } from 'contexts/Pools/ActivePool/types'; -import { IdentitiesController } from 'controllers/Identities'; +import { SubscriptionsController } from 'controllers/Subscriptions'; import { SyncController } from 'controllers/Sync'; -import type { AnyApi, MaybeAddress } from 'types'; +import { ActivePoolAccount } from 'model/Subscribe/ActivePoolAccount'; +import type { MaybeAddress, NetworkName, SystemChainId } from 'types'; import type { AccountActivePools, AccountPoolNominations, - AccountUnsubs, ActivePoolItem, DetailActivePool, } from './types'; export class ActivePoolsController { - // ------------------------------------------------------ - // Class members. - // ------------------------------------------------------ - // Pool ids that are being subscribed to. Keyed by address. static pools: Record = {}; - // Active pools that are being returned from subscriptions, keyed by account address, then pool - // id. - static activePools: Record = {}; - - // Active pool nominations, keyed by account address, then pool id. - static poolNominations: Record = {}; - - // Unsubscribe objects, keyed by account address, then pool id. - static #unsubs: Record = {}; + // Map from an address to its associated pool ids + static addressToPool: Record = {}; - // ------------------------------------------------------ - // Pool membership syncing. - // ------------------------------------------------------ - - // Subscribes to pools and unsubscribes from removed pools. + // Subscribes to new pools and unsubscribes from removed pools. static syncPools = async ( - api: ApiPromise, - peopleApi: ApiPromise, - peopleApiStatus: string, + network: NetworkName, address: MaybeAddress, newPools: ActivePoolItem[] ): Promise => { @@ -52,260 +31,125 @@ export class ActivePoolsController { } // Handle pools that have been removed. - this.handleRemovedPools(address, newPools); + this.handleRemovedPools(network, address); - const currentPools = this.getPools(address); + const currentPool = this.addressToPool[address]; // Determine new pools that need to be subscribed to. - const poolsAdded = newPools.filter( - (newPool) => !currentPools.find(({ id }) => id === newPool.id) - ); + const updatedPool = newPools.find((newPool) => currentPool === newPool.id) + ? false + : newPools[0]; - if (poolsAdded.length) { - // Subscribe to and add new pool data. - poolsAdded.forEach(async (pool) => { - this.pools[address] = currentPools.concat(pool); + if (updatedPool) { + this.pools[address] = newPools; - const unsub = await api.queryMulti( - [ - [api.query.nominationPools.bondedPools, pool.id], - [api.query.nominationPools.rewardPools, pool.id], - [api.query.system.account, pool.addresses.reward], - [api.query.staking.nominators, pool.addresses.stash], - ], - async ([ - bondedPool, - rewardPool, - accountData, - nominators, - ]): Promise => { - // NOTE: async: fetches identity data for roles. - await this.handleActivePoolCallback( - peopleApi, - peopleApiStatus, - address, - pool, - bondedPool, - rewardPool, - accountData - ); - this.handleNominatorsCallback(address, pool, nominators); + // Subscribe to and add new pool data. + SubscriptionsController.set( + network, + `activePool-${address}-${updatedPool.id}`, + new ActivePoolAccount(network, address, updatedPool) + ); - if ( - this.activePools?.[address]?.[pool.id] && - this.poolNominations?.[address]?.[pool.id] - ) { - document.dispatchEvent( - new CustomEvent('new-active-pool', { - detail: { - address, - pool: this.activePools[address][pool.id], - nominations: this.poolNominations[address][pool.id], - }, - }) - ); - } - } - ); - this.setUnsub(address, pool.id, unsub); - }); + // Add pool id to address mapping. + this.addressToPool[address] = updatedPool.id; } else { // Status: Pools Synced Completed. SyncController.dispatch('active-pools', 'complete'); } }; - // Handle active pool callback. - static handleActivePoolCallback = async ( - peopleApi: ApiPromise, - peopleApiStatus: string, - address: string, - pool: ActivePoolItem, - bondedPoolResult: AnyApi, - rewardPoolResult: AnyApi, - accountDataResult: AnyApi - ): Promise => { - const bondedPool = bondedPoolResult?.unwrapOr(undefined)?.toHuman(); - const rewardPool = rewardPoolResult?.unwrapOr(undefined)?.toHuman(); - const balance = accountDataResult.data; - const rewardAccountBalance = balance?.free.toString(); - - if (peopleApi && peopleApiStatus === 'ready') { - // Fetch identities for roles and expand `bondedPool` state to store them. - bondedPool.roleIdentities = await IdentitiesController.fetch( - peopleApi, - this.getUniqueRoleAddresses(bondedPool.roles) - ); - } - - // Only persist the active pool to class state (and therefore dispatch an event) if both the - // bonded pool and reward pool are returned. - if (bondedPool && rewardPool) { - const newPool = { - id: Number(pool.id), - addresses: pool.addresses, - bondedPool, - rewardPool, - rewardAccountBalance, - }; - - this.setActivePool(address, pool.id, newPool); - } else { - // Invalid pools were returned. To signal pool was synced, set active pool to `null`. - this.setActivePool(address, pool.id, null); - } - }; - - // Handle nominators callback. - static handleNominatorsCallback = ( - address: string, - pool: ActivePoolItem, - nominatorsResult: AnyApi - ): void => { - const maybeNewNominations = nominatorsResult.unwrapOr(null); - - const newNominations: Nominations = - maybeNewNominations === null - ? defaultPoolNominations - : { - targets: maybeNewNominations.targets.toHuman(), - submittedIn: maybeNewNominations.submittedIn.toHuman(), - }; - - this.setPoolNominations(address, pool.id, newNominations); - }; - // Remove pools that no longer exist. static handleRemovedPools = ( - address: string, - newPools: ActivePoolItem[] + network: NetworkName | SystemChainId, + address: string ): void => { - const currentPools = this.getPools(address); - - // Determine removed pools - current ones that no longer exist in `newPools`. - const poolsRemoved = currentPools.filter( - (pool) => !newPools.find((newPool) => newPool.id === pool.id) - ); + const currentPool = this.addressToPool[address]; - // Unsubscribe from removed pool subscriptions. - poolsRemoved.forEach((pool) => { - if (this.#unsubs?.[address]?.[pool.id]) { - this.#unsubs[address][pool.id](); - } - delete this.activePools[address][pool.id]; - delete this.poolNominations[address][pool.id]; - }); - - // Remove removed pools from class. - this.pools[address] = currentPools.filter( - (pool) => !poolsRemoved.includes(pool) - ); + if (currentPool) { + // Unsubscribe from removed pool subscription. + SubscriptionsController.remove( + network, + `activePool-${address}-${currentPool}` + ); - // Tidy up empty class state. - if (!this.pools[address].length) { + // Remove pool from class. + delete this.addressToPool[address]; delete this.pools[address]; } - - if (!this.activePools[address]) { - delete this.activePools[address]; - } - - if (!this.poolNominations[address]) { - delete this.poolNominations[address]; - } - if (!this.#unsubs[address]) { - delete this.#unsubs[address]; - } - }; - - // ------------------------------------------------------ - // Subscription handling. - // ------------------------------------------------------ - - // Unsubscribe from all subscriptions and reset class members. - static unsubscribe = (): void => { - Object.values(this.#unsubs).forEach((accountUnsubs) => { - Object.values(accountUnsubs).forEach((unsub) => { - unsub(); - }); - }); - - this.#unsubs = {}; }; // ------------------------------------------------------ // Getters. // ------------------------------------------------------ - // Gets pools for a provided address. - static getPools = (address: MaybeAddress): ActivePoolItem[] => { + // Gets pool for a provided address. + static getPool = ( + network: NetworkName, + address: MaybeAddress + ): ActivePoolItem | undefined => { if (!address) { - return []; + return undefined; } - return this.pools?.[address] || []; + const activePoolAccount = SubscriptionsController.get( + network, + `activePool-${address}-${this.addressToPool[address]}` + ) as ActivePoolAccount; + + return activePoolAccount?.pool || undefined; }; - // Gets active pools for a provided address. - static getActivePools = (address: MaybeAddress): AccountActivePools => { + // Gets active pool for a provided address. + static getActivePool = ( + network: NetworkName, + address: MaybeAddress + ): AccountActivePools => { if (!address) { return {}; } - return this.activePools?.[address] || {}; + const poolId = this.addressToPool[address]; + const activePool = SubscriptionsController.get( + network, + `activePool-${address}-${poolId}` + ) as ActivePoolAccount; + + if (!activePool) { + return {}; + } + + return { [poolId]: activePool?.activePool || null }; }; // Gets active pool nominations for a provided address. static getPoolNominations = ( + network: NetworkName, address: MaybeAddress ): AccountPoolNominations => { if (!address) { return {}; } - return this.poolNominations?.[address] || {}; - }; - - // Gets unique role addresses from a bonded pool's `roles` record. - static getUniqueRoleAddresses = (roles: PoolRoles): string[] => { - const roleAddresses: string[] = [ - ...new Set(Object.values(roles).filter((role) => role !== undefined)), - ]; - return roleAddresses; + const poolId = this.addressToPool[address]; + const activePool = SubscriptionsController.get( + network, + `activePool-${address}-${poolId}` + ) as ActivePoolAccount; + + return { + poolId: activePool?.poolNominations || defaultPoolNominations, + }; }; - // ------------------------------------------------------ - // Setters. - // ------------------------------------------------------ - - // Set an active pool for an address. - static setActivePool = ( - address: string, - poolId: string, - activePool: ActivePool | null - ): void => { - if (!this.activePools[address]) { - this.activePools[address] = {}; - } - this.activePools[address][poolId] = activePool; - }; - - // Set pool nominations for an address. - static setPoolNominations = ( - address: string, - poolId: string, - nominations: Nominations - ): void => { - if (!this.poolNominations[address]) { - this.poolNominations[address] = {}; - } - this.poolNominations[address][poolId] = nominations; - }; - - // Set unsub for an address and pool id. - static setUnsub = (address: string, poolId: string, unsub: VoidFn): void => { - if (!this.#unsubs[address]) { - this.#unsubs[address] = {}; - } - this.#unsubs[address][poolId] = unsub; - }; + // Gets all active pools for a provided network. + static getAllActivePools = (network: NetworkName) => + Object.fromEntries( + Object.entries(this.addressToPool).map(([addr, poolId]) => { + const activePoolAccount = SubscriptionsController.get( + network, + `activePool-${addr}-${poolId}` + ) as ActivePoolAccount; + + return [poolId, activePoolAccount?.activePool || null]; + }) + ); // ------------------------------------------------------ // Class helpers. diff --git a/packages/app/src/controllers/ActivePools/types.ts b/packages/app/src/controllers/ActivePools/types.ts index b838646fac..2753a60ad3 100644 --- a/packages/app/src/controllers/ActivePools/types.ts +++ b/packages/app/src/controllers/ActivePools/types.ts @@ -1,7 +1,6 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { VoidFn } from '@polkadot/api/types'; import type { Nominations } from 'contexts/Balances/types'; import type { ActivePool } from 'contexts/Pools/ActivePool/types'; @@ -22,5 +21,3 @@ export interface ActivePoolItem { export type AccountActivePools = Record; export type AccountPoolNominations = Record; - -export type AccountUnsubs = Record; diff --git a/packages/app/src/controllers/Balances/index.ts b/packages/app/src/controllers/Balances/index.ts index 552db93ca7..f115cd6d33 100644 --- a/packages/app/src/controllers/Balances/index.ts +++ b/packages/app/src/controllers/Balances/index.ts @@ -1,61 +1,23 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { ApiPromise } from '@polkadot/api'; -import type { VoidFn } from '@polkadot/api/types'; -import { rmCommas } from '@w3ux/utils'; -import BigNumber from 'bignumber.js'; -import type { - ActiveBalance, - Balances, - Ledger, - Nominations, - UnlockChunkRaw, -} from 'contexts/Balances/types'; -import type { PoolMembership } from 'contexts/Pools/types'; -import type { PayeeConfig, PayeeOptions } from 'contexts/Setup/types'; +import type { ActiveBalance } from 'contexts/Balances/types'; +import { SubscriptionsController } from 'controllers/Subscriptions'; import { SyncController } from 'controllers/Sync'; -import { stringToBn } from 'library/Utils'; -import type { AnyApi, MaybeAddress } from 'types'; -import { defaultNominations } from './defaults'; +import { AccountBalances } from 'model/Subscribe/AccountBalances'; +import type { NetworkName } from 'types'; export class BalancesController { - // ------------------------------------------------------ - // Class members. - // ------------------------------------------------------ - // Accounts that are being subscribed to. static accounts: string[] = []; - // Account ledgers, populated by api callbacks. - static ledgers: Record = {}; - - // Account balances, populated by api callbacks. - static balances: Record = {}; - - // Account payees, populated by api callbacks. - static payees: Record = {}; - - // Account pool membership and claim commissions, populated by api callbacks. - static poolMemberships: Record = {}; - - // Account nominations, populated by api callbacks. - static nominations: Record = {}; - - // Unsubscribe objects. - static #unsubs: Record = {}; - - // ------------------------------------------------------ - // Account syncing. - // ------------------------------------------------------ - // Subscribes new accounts and unsubscribes & removes removed accounts. static syncAccounts = async ( - api: ApiPromise, + network: NetworkName, newAccounts: string[] ): Promise => { // Handle accounts that have been removed. - this.handleRemovedAccounts(newAccounts); + this.handleRemovedAccounts(network, newAccounts); // Determine new accounts that need to be subscribed to. const accountsAdded = newAccounts.filter( @@ -73,237 +35,55 @@ export class BalancesController { // Subscribe to and add new accounts data. accountsAdded.forEach(async (address) => { this.accounts.push(address); - const unsub = await api.queryMulti( - [ - [api.query.staking.ledger, address], - [api.query.system.account, address], - [api.query.balances.locks, address], - [api.query.staking.payee, address], - [api.query.nominationPools.poolMembers, address], - [api.query.nominationPools.claimPermissions, address], - [api.query.staking.nominators, address], - ], - async ([ - ledgerResult, - accountResult, - locksResult, - payeeResult, - poolMembersResult, - claimPermissionsResult, - nominatorsResult, - ]): Promise => { - this.handleLedgerCallback(address, ledgerResult); - this.handleAccountCallback(address, accountResult, locksResult); - this.handlePayeeCallback(address, payeeResult); - - // NOTE: async: contains runtime call for pending rewards. - await this.handlePoolMembershipCallback( - api, - address, - poolMembersResult, - claimPermissionsResult - ); - this.handleNominations(address, nominatorsResult); - - // Send updated account state back to UI. - document.dispatchEvent( - new CustomEvent('new-account-balance', { - detail: { - address, - ledger: this.ledgers[address], - balances: this.balances[address], - payee: this.payees[address], - poolMembership: this.poolMemberships[address], - nominations: this.nominations[address], - }, - }) - ); - } + SubscriptionsController.set( + network, + `accountBalances-${address}`, + new AccountBalances(network, address) ); - this.#unsubs[address] = unsub; }); }; // Remove accounts that no longer exist. - static handleRemovedAccounts = (newAccounts: string[]): void => { + static handleRemovedAccounts = ( + network: NetworkName, + newAccounts: string[] + ): void => { // Determine removed accounts. const accountsRemoved = this.accounts.filter( (account) => !newAccounts.includes(account) ); // Unsubscribe from removed account subscriptions. accountsRemoved.forEach((account) => { - if (this.#unsubs[account]) { - this.#unsubs[account](); - } - delete this.#unsubs[account]; - delete this.ledgers[account]; - delete this.balances[account]; - delete this.payees[account]; - delete this.poolMemberships[account]; - delete this.nominations[account]; + SubscriptionsController.remove(network, `accountBalances-${account}`); }); + // Remove removed accounts from class. this.accounts = this.accounts.filter( (account) => !accountsRemoved.includes(account) ); }; - // Handle ledger callback. - static handleLedgerCallback = (address: string, result: AnyApi): void => { - const ledger = result.unwrapOr(null); - - // If ledger is null, remove from class data and exit early. - if (ledger === null) { - delete this.ledgers[address]; - return; - } - - const { stash, total, active, unlocking } = ledger; - - // Send stash address to UI as event if not presently imported. - if (!this.accounts.includes(stash.toString())) { - document.dispatchEvent( - new CustomEvent('new-external-account', { - detail: { address: stash.toString() }, - }) - ); - } - - this.ledgers[address] = { - stash: stash.toString(), - active: stringToBn(active.toString()), - total: stringToBn(total.toString()), - unlocking: unlocking.toHuman().map(({ era, value }: UnlockChunkRaw) => ({ - era: Number(rmCommas(era)), - value: stringToBn(value), - })), - }; - }; - - // Handle account callback. - static handleAccountCallback = ( - address: string, - { data: accountData, nonce }: AnyApi, - locksResult: AnyApi - ): void => { - this.balances[address] = { - nonce: nonce.toNumber(), - balance: { - free: stringToBn(accountData.free.toString()), - reserved: stringToBn(accountData.reserved.toString()), - frozen: stringToBn(accountData.frozen.toString()), - }, - locks: locksResult - .toHuman() - .map((lock: { id: string; amount: string }) => ({ - ...lock, - id: lock.id.trim(), - amount: stringToBn(lock.amount), - })), - }; - }; - - // Handle payee callback. payee with `Account` type is returned as an key value pair, with all - // others strings. This function handles both cases and formats into a unified structure. - static handlePayeeCallback = (address: string, result: AnyApi): void => { - const payeeHuman = result.toHuman(); - let payeeFinal: PayeeConfig; - - if (payeeHuman !== null) { - if (typeof payeeHuman === 'string') { - const destination = payeeHuman as PayeeOptions; - payeeFinal = { - destination, - account: null, - }; - } else { - const payeeEntry = Object.entries(payeeHuman); - const destination = `${payeeEntry[0][0]}` as PayeeOptions; - const account = `${payeeEntry[0][1]}` as MaybeAddress; - payeeFinal = { - destination, - account, - }; - } - this.payees[address] = payeeFinal; - } - }; - - // Handle pool membership and claim commission callback. - static handlePoolMembershipCallback = async ( - api: ApiPromise, - address: string, - poolMembersResult: AnyApi, - claimPermissionsResult: AnyApi - ): Promise => { - // If pool membership is `null`, remove pool membership data from class data and exit early. - // This skips claim permission data as well as user would not have claim permissions if they are - // not in a pool. - const membership = poolMembersResult?.unwrapOr(undefined)?.toHuman(); - if (!membership) { - delete this.poolMemberships[address]; - return; - } - - // Format pool membership data. - const unlocking = Object.entries(membership?.unbondingEras || {}).map( - ([e, v]) => ({ - era: Number(rmCommas(e as string)), - value: new BigNumber(rmCommas(v as string)), - }) - ); - membership.points = rmCommas(membership?.points || '0'); - const balance = new BigNumber( - ( - await api.call.nominationPoolsApi.pointsToBalance( - membership.poolId, - membership.points - ) - )?.toString() || '0' - ); - const claimPermission = - claimPermissionsResult?.toString() || 'Permissioned'; - - // Persist formatted pool membership data to class. - this.poolMemberships[address] = { - ...membership, - address, - balance, - claimPermission, - unlocking, - }; - }; - - // Handle nominations callback. - static handleNominations = ( - address: string, - nominatorsResult: AnyApi - ): void => { - const nominators = nominatorsResult.unwrapOr(null); - - this.nominations[address] = - nominators === null - ? defaultNominations - : { - targets: nominators.targets.toHuman(), - submittedIn: nominators.submittedIn.toHuman(), - }; - }; - - // Gets an `ActiveBalance` from class members for the given address if it exists. - static getAccountBalances = (address: string): ActiveBalance | undefined => { - const ledger = this.ledgers[address]; - const balances = this.balances[address]; - const payee = this.payees[address]; - const poolMembership = this.poolMemberships[address]; - const nominations = this.nominations[address]; - - if (balances === undefined) { - // Account info has not synced yet. Note that `ledger` may not exist and therefore cannot be - // tested. + // Gets an `AccountBalances` subscription from class members for the given address if it exists. + static getAccountBalances = ( + network: NetworkName, + address: string + ): ActiveBalance | undefined => { + const accountBalances = SubscriptionsController.get( + network, + `accountBalances-${address}` + ) as AccountBalances; + + // Account info has not synced yet - exit early. + if (!accountBalances) { return undefined; } + const ledger = accountBalances.ledger; + const balances = accountBalances.balance; + const payee = accountBalances.payee; + const poolMembership = accountBalances.poolMembership; + const nominations = accountBalances.nominations; + return { ledger, balances, @@ -313,22 +93,6 @@ export class BalancesController { }; }; - // ------------------------------------------------------ - // Subscription handling. - // ------------------------------------------------------ - - // Unsubscribe from all subscriptions and reset class members. - static unsubscribe = (): void => { - Object.values(this.#unsubs).forEach((unsub) => { - unsub(); - }); - this.#unsubs = {}; - }; - - // ------------------------------------------------------ - // Class helpers. - // ------------------------------------------------------ - // Checks if event detailis a valid `new-account-balance` event. Note that `ledger` may not exist // and therefore cannot be tested. static isValidNewAccountBalanceEvent = ( diff --git a/packages/app/src/controllers/Identities/index.ts b/packages/app/src/controllers/Identities/index.ts index d21f9c0445..357d6e7f62 100644 --- a/packages/app/src/controllers/Identities/index.ts +++ b/packages/app/src/controllers/Identities/index.ts @@ -1,22 +1,24 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { ApiPromise } from '@polkadot/api'; import type { AnyJson } from '@w3ux/types'; +import type { PapiApi } from 'model/Api/types'; +import { IdentityOfMulti } from 'model/Query/IdentityOfMulti'; +import { SuperOfMulti } from 'model/Query/SuperOfMulti'; import type { AnyApi } from 'types'; export class IdentitiesController { - static fetch = async (api: ApiPromise, addresses: string[]) => { + static fetch = async (pApi: PapiApi, addresses: string[]) => { // Fetches identities for addresses. const fetchBase = async () => { - const result = (await api.query.identity.identityOf.multi(addresses)).map( - (identity) => identity.toHuman() - ); + const addressesMulti: [string][] = addresses.map((address) => [address]); + const result = await new IdentityOfMulti(pApi, addressesMulti).fetch(); // Take identity data (first index) of results. - const data = result.map( - (resultArray: AnyJson | null) => resultArray?.[0] || null - ); + const data = + result?.map( + (resultArray: AnyJson | null) => resultArray?.[0] || null + ) || []; return Object.fromEntries( data @@ -27,26 +29,28 @@ export class IdentitiesController { // Fetch an array of super accounts and their identities. const fetchSupers = async () => { - const supersRaw = (await api.query.identity.superOf.multi(addresses)).map( - (superOf) => superOf.toHuman() - ); + const addressesMulti: [string][] = addresses.map((address) => [address]); + const supersRawMulti = await new SuperOfMulti( + pApi, + addressesMulti + ).fetch(); const supers = Object.fromEntries( - supersRaw + (supersRawMulti || []) .map((k, i) => [ addresses[i], { superOf: k, }, ]) - .filter(([, { superOf }]: AnyApi) => superOf !== null) + .filter(([, { superOf }]: AnyApi) => superOf !== undefined) ); - const superIdentities = ( - await api.query.identity.identityOf.multi( - Object.values(supers).map(({ superOf }: AnyApi) => superOf[0]) - ) - ).map((superIdentity) => superIdentity.toHuman()); + const superOfMulti: [string][] = Object.values(supers).map( + ({ superOf }: AnyApi) => [superOf[0]] + ); + const superIdentities = + (await new IdentityOfMulti(pApi, superOfMulti).fetch()) || []; // Take identity data (first index) of results. const data = superIdentities.map( diff --git a/packages/app/src/controllers/Subscriptions/index.ts b/packages/app/src/controllers/Subscriptions/index.ts index c71fa65523..b4f2cbdcb1 100644 --- a/packages/app/src/controllers/Subscriptions/index.ts +++ b/packages/app/src/controllers/Subscriptions/index.ts @@ -45,7 +45,7 @@ export class SubscriptionsController { // Sets a new subscription for a network. static set( - network: NetworkName, + network: NetworkName | SystemChainId, subscriptionId: string, subscription: Subscription ): void { diff --git a/packages/app/src/controllers/Subscriptions/types.ts b/packages/app/src/controllers/Subscriptions/types.ts index c050a3fc74..fab4aca5f1 100644 --- a/packages/app/src/controllers/Subscriptions/types.ts +++ b/packages/app/src/controllers/Subscriptions/types.ts @@ -1,18 +1,32 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only +import type { AccountBalances } from 'model/Subscribe/AccountBalances'; +import type { AccountProxies } from 'model/Subscribe/AccountProxies'; import type { ActiveEra } from 'model/Subscribe/ActiveEra'; +import type { ActivePoolAccount } from 'model/Subscribe/ActivePoolAccount'; import type { BlockNumber } from 'model/Subscribe/BlockNumber'; +import type { Bonded } from 'model/Subscribe/Bonded'; +import type { FastUnstakeConfig } from 'model/Subscribe/FastUnstakeConfig'; +import type { FastUnstakeQueue } from 'model/Subscribe/FastUnstakeQueue'; import type { NetworkMetrics } from 'model/Subscribe/NetworkMetrics'; +import type { PoolMembersMulti } from 'model/Subscribe/PoolMembersMulti'; import type { PoolsConfig } from 'model/Subscribe/PoolsConfig'; import type { StakingMetrics } from 'model/Subscribe/StakingMetrics'; // Define all possible subscription classes. export type Subscription = + | AccountBalances + | AccountProxies | ActiveEra + | ActivePoolAccount | BlockNumber + | Bonded + | FastUnstakeConfig + | FastUnstakeQueue | NetworkMetrics | PoolsConfig + | PoolMembersMulti | StakingMetrics; // the record of subscriptions, keyed by tabId. diff --git a/packages/app/src/hooks/useActiveBalances/index.tsx b/packages/app/src/hooks/useActiveBalances/index.tsx index 3f2f18f951..0cf3d54188 100644 --- a/packages/app/src/hooks/useActiveBalances/index.tsx +++ b/packages/app/src/hooks/useActiveBalances/index.tsx @@ -43,7 +43,7 @@ export const useActiveBalances = ({ // Gets an active balance's balance. const getBalance = (address: MaybeAddress) => { if (address) { - const maybeBalance = activeBalances[address]?.balances.balance; + const maybeBalance = activeBalances[address]?.balances?.balance; if (maybeBalance) { return maybeBalance; } @@ -62,7 +62,7 @@ export const useActiveBalances = ({ // Gets an active balance's locks. const getLocks = (address: MaybeAddress): BalanceLocks => { if (address) { - const maybeLocks = activeBalances[address]?.balances.locks; + const maybeLocks = activeBalances[address]?.balances?.locks; if (maybeLocks) { return { locks: maybeLocks, maxLock: getMaxLock(maybeLocks) }; } @@ -170,7 +170,10 @@ export const useActiveBalances = ({ for (const account of uniqueAccounts) { // Adds an active balance record if it exists in `BalancesController`. if (account) { - const accountBalances = BalancesController.getAccountBalances(account); + const accountBalances = BalancesController.getAccountBalances( + network, + account + ); if (accountBalances) { newActiveBalances[account] = accountBalances; } diff --git a/packages/app/src/hooks/useActivePools/index.tsx b/packages/app/src/hooks/useActivePools/index.tsx index bfef924560..baf59242d9 100644 --- a/packages/app/src/hooks/useActivePools/index.tsx +++ b/packages/app/src/hooks/useActivePools/index.tsx @@ -18,14 +18,14 @@ export const useActivePools = ({ onCallback, who }: ActivePoolsProps) => { // Stores active pools. const [activePools, setActivePools] = useState( - ActivePoolsController.getActivePools(who) + ActivePoolsController.getActivePool(network, who) ); const activePoolsRef = useRef(activePools); // Store nominations of active pools. const [poolNominations, setPoolNominations] = useState( - ActivePoolsController.getPoolNominations(who) + ActivePoolsController.getPoolNominations(network, who) ); const poolNominationsRef = useRef(poolNominations); @@ -58,7 +58,8 @@ export const useActivePools = ({ onCallback, who }: ActivePoolsProps) => { }; // Get an active pool. - const getActivePools = (poolId: string) => activePools?.[poolId] || null; + const getActivePool = (poolId: string) => + activePools?.[Number(poolId)] || null; // Get an active pool's nominations. const getPoolNominations = (poolId: string) => @@ -73,12 +74,12 @@ export const useActivePools = ({ onCallback, who }: ActivePoolsProps) => { // Update state on account change. useEffect(() => { setStateWithRef( - ActivePoolsController.getActivePools(who), + ActivePoolsController.getActivePool(network, who), setActivePools, activePoolsRef ); setStateWithRef( - ActivePoolsController.getPoolNominations(who), + ActivePoolsController.getPoolNominations(network, who), setPoolNominations, poolNominationsRef ); @@ -91,7 +92,7 @@ export const useActivePools = ({ onCallback, who }: ActivePoolsProps) => { return { activePools, activePoolsRef, - getActivePools, + getActivePool, getPoolNominations, }; }; diff --git a/packages/app/src/hooks/useBatchCall/index.tsx b/packages/app/src/hooks/useBatchCall/index.tsx index 0160a942ea..ae1613998b 100644 --- a/packages/app/src/hooks/useBatchCall/index.tsx +++ b/packages/app/src/hooks/useBatchCall/index.tsx @@ -2,36 +2,45 @@ // SPDX-License-Identifier: GPL-3.0-only import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useApi } from 'contexts/Api'; +import { useNetwork } from 'contexts/Network'; +import { ApiController } from 'controllers/Api'; import { useProxySupported } from 'hooks/useProxySupported'; +import type { UnsafeTx } from 'hooks/useSubmitExtrinsic/types'; import type { AnyApi, MaybeAddress } from 'types'; export const useBatchCall = () => { - const { api } = useApi(); + const { network } = useNetwork(); const { activeProxy } = useActiveAccounts(); const { isProxySupported } = useProxySupported(); - const newBatchCall = (txs: AnyApi[], from: MaybeAddress): AnyApi => { - if (!api) { + const newBatchCall = (txs: UnsafeTx[], from: MaybeAddress): AnyApi => { + const { pApi } = ApiController.get(network); + + if (!pApi) { return undefined; } from = from || ''; + const batchTx = pApi.tx.Utility.batch({ + calls: txs.map((tx) => tx.decodedCall), + }); - if (activeProxy && isProxySupported(api.tx.utility.batch(txs), from)) { - return api?.tx.utility.batch( - txs.map((tx) => - api.tx.proxy.proxy( - { - id: from, - }, - null, - tx - ) - ) - ); + if (activeProxy && isProxySupported(batchTx, from)) { + return pApi.tx.Utility.batch({ + calls: txs.map( + (tx) => + pApi.tx.Proxy.proxy({ + real: { + type: 'Id', + value: from, + }, + force_proxy_type: undefined, + call: tx.decodedCall, + }).decodedCall + ), + }); } - return api?.tx.utility.batch(txs); + return batchTx; }; return { diff --git a/packages/app/src/hooks/useBondGreatestFee/index.tsx b/packages/app/src/hooks/useBondGreatestFee/index.tsx index a9f2f6b26a..3907b88906 100644 --- a/packages/app/src/hooks/useBondGreatestFee/index.tsx +++ b/packages/app/src/hooks/useBondGreatestFee/index.tsx @@ -3,17 +3,14 @@ import BigNumber from 'bignumber.js'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useApi } from 'contexts/Api'; +import { useNetwork } from 'contexts/Network'; import { useTransferOptions } from 'contexts/TransferOptions'; +import { ApiController } from 'controllers/Api'; import { useEffect, useMemo, useState } from 'react'; import type { BondFor } from 'types'; -interface Props { - bondFor: BondFor; -} - -export const useBondGreatestFee = ({ bondFor }: Props) => { - const { api } = useApi(); +export const useBondGreatestFee = ({ bondFor }: { bondFor: BondFor }) => { + const { network } = useNetwork(); const { activeAccount } = useActiveAccounts(); const { feeReserve, getTransferOptions } = useTransferOptions(); const transferOptions = useMemo( @@ -38,26 +35,31 @@ export const useBondGreatestFee = ({ bondFor }: Props) => { // estimate the largest possible tx fee based on users free balance. const txLargestFee = async () => { + const { pApi } = ApiController.get(network); + const bond = BigNumber.max( transferrableBalance.minus(feeReserve), 0 ).toString(); let tx = null; - if (!api) { + if (!pApi) { return new BigNumber(0); } if (bondFor === 'pool') { - tx = api.tx.nominationPools.bondExtra({ - FreeBalance: bond, + tx = pApi.tx.NominationPools.bond_extra({ + extra: { + type: 'FreeBalance', + value: BigInt(bond), + }, }); } else if (bondFor === 'nominator') { - tx = api.tx.staking.bondExtra(bond); + tx = pApi.tx.Staking.bond_extra({ max_additional: BigInt(bond) }); } if (tx) { - const { partialFee } = await tx.paymentInfo(activeAccount || ''); - return new BigNumber(partialFee.toString()); + const { partial_fee } = await tx.getPaymentInfo(activeAccount || ''); + return new BigNumber(partial_fee.toString()); } return new BigNumber(0); }; diff --git a/packages/app/src/hooks/useBuildPayload/index.tsx b/packages/app/src/hooks/useBuildPayload/index.tsx deleted file mode 100644 index 3105f361e4..0000000000 --- a/packages/app/src/hooks/useBuildPayload/index.tsx +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import { merkleizeMetadata } from '@polkadot-api/merkleize-metadata'; -import type { ApiPromise } from '@polkadot/api'; -import { objectSpread, u8aToHex } from '@polkadot/util'; -import type { AnyJson } from '@w3ux/types'; -import { useApi } from 'contexts/Api'; -import { useBalances } from 'contexts/Balances'; -import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; -import { useTxMeta } from 'contexts/TxMeta'; -import type { AnyApi } from 'types'; - -export const useBuildPayload = () => { - const { api } = useApi(); - const { getNonce } = useBalances(); - const { setTxPayload } = useTxMeta(); - const { getAccount } = useImportedAccounts(); - - // Build metadata hash and return updated payload. - const fetchMetadataHash = async (a: ApiPromise, p: AnyJson) => { - const metadata = await a.call.metadata.metadataAtVersion(15); - const { specName, specVersion } = a.runtimeVersion; - - const opts = { - base58Prefix: Number(a.consts.system.ss58Prefix.toString()), - decimals: a.registry.chainDecimals[0], - specName: specName.toString(), - specVersion: specVersion.toNumber(), - tokenSymbol: a.registry.chainTokens[0], - }; - - const merkleizedMetadata = merkleizeMetadata(metadata.toHex(), opts); - const metadataHash = u8aToHex(merkleizedMetadata.digest()); - const payload = objectSpread({}, p, { - metadataHash, - mode: 1, - withSignedTransaction: true, - }); - const newPayload = a.registry.createType('ExtrinsicPayload', payload); - - return { - newPayload, - newTxMetadata: merkleizedMetadata.getProofForExtrinsicPayload( - u8aToHex(newPayload.toU8a(true)) - ), - }; - }; - - // Build and set payload of the transaction and store it in TxMetaContext. - const buildPayload = async (tx: AnyApi, from: string, uid: number) => { - if (api && tx) { - const accountMeta = getAccount(from); - const source = accountMeta?.source; - - const lastHeader = await api.rpc.chain.getHeader(); - const blockNumber = api.registry.createType( - 'BlockNumber', - lastHeader.number.toNumber() - ); - const method = api.createType('Call', tx); - const era = api.registry.createType('ExtrinsicEra', { - current: lastHeader.number.toNumber(), - period: 64, - }); - - const accountNonce = getNonce(from); - const nonce = api.registry.createType('Compact', accountNonce); - - // Construct the payload value. - const payloadJson: AnyJson = { - specVersion: api.runtimeVersion.specVersion.toHex(), - transactionVersion: api.runtimeVersion.transactionVersion.toHex(), - runtimeVersion: api.runtimeVersion, - version: api.extrinsicVersion, - address: from, - blockHash: lastHeader.hash.toHex(), - blockNumber: blockNumber.toHex(), - era: era.toHex(), - genesisHash: api.genesisHash.toHex(), - method: method.toHex(), - nonce: nonce.toHex(), - signedExtensions: api.registry.signedExtensions, - tip: api.registry.createType('Compact', 0).toHex(), - }; - - console.log(payloadJson); - - let payload; - let txMetadata = null; - - // If the source is `ledger`, add the metadata hash to the payload. - if (source === 'ledger') { - const { newPayload, newTxMetadata } = await fetchMetadataHash( - api, - payloadJson - ); - payload = newPayload; - txMetadata = newTxMetadata; - } else { - // Create the payload raw. - payload = api.registry.createType('ExtrinsicPayload', payloadJson, { - version: payloadJson.version, - }); - } - - // Persist both the payload and the payload bytes in state, indexed by its uid. - setTxPayload(txMetadata, payload, payloadJson, uid); - } - }; - - return { - buildPayload, - }; -}; diff --git a/packages/app/src/hooks/useCreatePoolAccounts/index.tsx b/packages/app/src/hooks/useCreatePoolAccounts/index.tsx index e3bdc79da4..16ccd601db 100644 --- a/packages/app/src/hooks/useCreatePoolAccounts/index.tsx +++ b/packages/app/src/hooks/useCreatePoolAccounts/index.tsx @@ -1,14 +1,17 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import { bnToU8a, u8aConcat } from '@polkadot/util'; +import { AccountId } from '@polkadot-api/substrate-bindings'; +import { bnToU8a, stringToU8a, u8aConcat } from '@polkadot/util'; import BigNumber from 'bignumber.js'; import { BN } from 'bn.js'; -import { EmptyH256, ModPrefix, U32Opts } from 'consts'; import { useApi } from 'contexts/Api'; export const useCreatePoolAccounts = () => { - const { api, consts } = useApi(); + const { + consts, + chainSpecs: { ss58Format }, + } = useApi(); const { poolsPalletId } = consts; // Generates pool stash and reward accounts. Assumes `poolsPalletId` is synced. @@ -21,21 +24,16 @@ export const useCreatePoolAccounts = () => { }; const createAccount = (poolId: BigNumber, index: number): string => { - if (!api) { - return ''; - } - return api.registry - .createType( - 'AccountId32', - u8aConcat( - ModPrefix, - poolsPalletId, - new Uint8Array([index]), - bnToU8a(new BN(poolId.toString()), U32Opts), - EmptyH256 - ) - ) - .toString(); + const key = u8aConcat( + stringToU8a('modl'), + poolsPalletId, + new Uint8Array([index]), + bnToU8a(new BN(poolId.toString()), { bitLength: 32, isLe: true }), + new Uint8Array(32) + ); + + const codec = AccountId(ss58Format); + return codec.dec(key); }; return createPoolAccounts; diff --git a/packages/app/src/hooks/useProxySupported/index.tsx b/packages/app/src/hooks/useProxySupported/index.tsx index 3b11229d2b..48f886b8b9 100644 --- a/packages/app/src/hooks/useProxySupported/index.tsx +++ b/packages/app/src/hooks/useProxySupported/index.tsx @@ -9,7 +9,8 @@ import { import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useBonded } from 'contexts/Bonded'; import { useProxies } from 'contexts/Proxies'; -import type { AnyApi, MaybeAddress } from 'types'; +import type { UnsafeTx } from 'hooks/useSubmitExtrinsic/types'; +import type { MaybeAddress } from 'types'; export const useProxySupported = () => { const { getBondedAccount } = useBonded(); @@ -22,32 +23,32 @@ export const useProxySupported = () => { UnsupportedIfUniqueController.includes(c) && getBondedAccount(f) !== f; // Determine whether the provided tx is proxy supported. - const isProxySupported = (tx: AnyApi, delegator: MaybeAddress) => { + const isProxySupported = (tx: UnsafeTx, delegator: MaybeAddress) => { // if already wrapped, return. if ( - tx?.method.toHuman().section === 'proxy' && - tx?.method.toHuman().method === 'proxy' + tx?.decodedCall.type === 'Proxy' && + tx?.decodedCall.value.type === 'proxy' ) { return true; } const proxyDelegate = getProxyDelegate(delegator, activeProxy); const proxyType = proxyDelegate?.proxyType || ''; - const pallet = tx?.method.toHuman().section; - const method = tx?.method.toHuman().method; + const pallet: string = (tx?.decodedCall.type || '').toLowerCase(); + const method: string = (tx?.decodedCall.value.type || '').toLowerCase(); const call = `${pallet}.${method}`; // If a batch call, test if every inner call is a supported proxy call. if (call === 'utility.batch') { - return (tx?.method?.toHuman()?.args?.calls || []) + return (tx?.decodedCall.value?.value?.calls || []) .map((c: AnyJson) => ({ - pallet: c.section, - method: c.method, + pallet: c.type, + method: c.value.type, })) .every( (c: AnyJson) => (isSupportedProxyCall(proxyType, c.pallet, c.method) || - (c.pallet === 'proxy' && c.method === 'proxy')) && + (c.pallet === 'Proxy' && c.method === 'proxy')) && !controllerNotSupported(`${pallet}.${method}`, delegator) ); } diff --git a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx index 0b988244f1..e50233f97b 100644 --- a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx +++ b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx @@ -2,20 +2,42 @@ // SPDX-License-Identifier: GPL-3.0-only import { useExtensions } from '@w3ux/react-connect-kit'; +import type { LedgerAccount } from '@w3ux/react-connect-kit/types'; +import { formatAccountSs58 } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import { DappName, ManualSigners } from 'consts'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useApi } from 'contexts/Api'; +import { useBalances } from 'contexts/Balances'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { useLedgerHardware } from 'contexts/LedgerHardware'; +import { getLedgerApp } from 'contexts/LedgerHardware/Utils'; +import { useNetwork } from 'contexts/Network'; +import { usePrompt } from 'contexts/Prompt'; import { useTxMeta } from 'contexts/TxMeta'; +import { useWalletConnect } from 'contexts/WalletConnect'; +import { ApiController } from 'controllers/Api'; import { NotificationsController } from 'controllers/Notifications'; +import { useProxySupported } from 'hooks/useProxySupported'; +import { LedgerSigner } from 'library/Signers/LedgerSigner'; +import { VaultSigner } from 'library/Signers/VaultSigner'; +import type { + VaultSignatureResult, + VaultSignStatus, +} from 'library/Signers/VaultSigner/types'; +import { SignPrompt } from 'library/SubmitTx/ManualSign/Vault/SignPrompt'; +import type { PolkadotSigner } from 'polkadot-api'; +import { AccountId, InvalidTxError } from 'polkadot-api'; +import { + connectInjectedExtension, + getPolkadotSignerFromPjs, +} from 'polkadot-api/pjs-signer'; import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import type { AnyApi } from 'types'; -import { useBuildPayload } from '../useBuildPayload'; -import { useProxySupported } from '../useProxySupported'; -import type { UseSubmitExtrinsic, UseSubmitExtrinsicProps } from './types'; +import type { + UnsafeTx, + UseSubmitExtrinsic, + UseSubmitExtrinsicProps, +} from './types'; export const useSubmitExtrinsic = ({ tx, @@ -25,27 +47,23 @@ export const useSubmitExtrinsic = ({ callbackInBlock, }: UseSubmitExtrinsicProps): UseSubmitExtrinsic => { const { t } = useTranslation('library'); - const { api } = useApi(); - const { buildPayload } = useBuildPayload(); + const { + network, + networkData: { units, unit }, + } = useNetwork(); + const { getNonce } = useBalances(); + const { signWcTx } = useWalletConnect(); const { activeProxy } = useActiveAccounts(); const { extensionsStatus } = useExtensions(); const { isProxySupported } = useProxySupported(); + const { openPromptWith, closePrompt } = usePrompt(); const { handleResetLedgerTask } = useLedgerHardware(); const { addPendingNonce, removePendingNonce } = useTxMeta(); const { getAccount, requiresManualSign } = useImportedAccounts(); - const { - txFees, - setTxFees, - setSender, - getTxPayload, - getTxSignature, - setTxSignature, - resetTxPayload, - incrementPayloadUid, - } = useTxMeta(); + const { txFees, setTxFees, setSender, incrementPayloadUid } = useTxMeta(); // Store given tx as a ref. - const txRef = useRef(tx); + const txRef = useRef(tx); // Store given submit address as a ref. const fromRef = useRef(from || ''); @@ -61,10 +79,12 @@ export const useSubmitExtrinsic = ({ // If proxy account is active, wrap tx in a proxy call and set the sender to the proxy account. const wrapTxIfActiveProxy = () => { + const { pApi } = ApiController.get(network); + // if already wrapped, update fromRef and return. if ( - txRef.current?.method.toHuman().section === 'proxy' && - txRef.current?.method.toHuman().method === 'proxy' + txRef.current?.decodedCall.type === 'Proxy' && + txRef.current?.decodedCall.value.type === 'proxy' ) { if (activeProxy) { fromRef.current = activeProxy; @@ -73,7 +93,7 @@ export const useSubmitExtrinsic = ({ } if ( - api && + pApi && activeProxy && txRef.current && isProxySupported(txRef.current, fromRef.current) @@ -84,20 +104,21 @@ export const useSubmitExtrinsic = ({ // Do not wrap batch transactions. Proxy calls should already be wrapping each tx within the // batch via `useBatchCall`. if ( - txRef.current?.method.toHuman().section === 'utility' && - txRef.current?.method.toHuman().method === 'batch' + txRef.current?.decodedCall.type === 'Utility' && + txRef.current?.decodedCall.value.type === 'batch' ) { return; } // Not a batch transaction: wrap tx in proxy call. - txRef.current = api.tx.proxy.proxy( - { - id: from, + txRef.current = pApi.tx.Proxy.proxy({ + real: { + type: 'Id', + value: from || '', }, - null, - txRef.current - ); + force_proxy_type: undefined, + call: txRef.current.decodedCall, + }); } }; @@ -106,8 +127,10 @@ export const useSubmitExtrinsic = ({ if (txRef.current === null) { return; } + // get payment info - const { partialFee } = await txRef.current.paymentInfo(fromRef.current); + const partialFee = (await txRef.current.getPaymentInfo(fromRef.current)) + .partial_fee; const partialFeeBn = new BigNumber(partialFee.toString()); // give tx fees to global useTxMeta context @@ -119,24 +142,16 @@ export const useSubmitExtrinsic = ({ // Extrinsic submission handler. const onSubmit = async () => { const account = getAccount(fromRef.current); - if ( - account === null || - submitting || - !shouldSubmit || - !api || - (requiresManualSign(fromRef.current) && !getTxSignature()) - ) { + if (account === null || submitting || !shouldSubmit) { return; } - const nonce = ( - await api.rpc.system.accountNextIndex(fromRef.current) - ).toHuman(); - + const nonce = String(getNonce(fromRef.current)); const { source } = account; + const isManualSigner = ManualSigners.includes(source); // if `activeAccount` is imported from an extension, ensure it is enabled. - if (!ManualSigners.includes(source)) { + if (!isManualSigner) { const isInstalled = Object.entries(extensionsStatus).find( ([id, status]) => id === source && status === 'connected' ); @@ -176,31 +191,26 @@ export const useSubmitExtrinsic = ({ } }; - const onFinalizedEvent = (method: string) => { - if (method === 'ExtrinsicSuccess') { - NotificationsController.emit({ - title: t('finalized'), - subtitle: t('transactionSuccessful'), - }); - } else if (method === 'ExtrinsicFailed') { - NotificationsController.emit({ - title: t('failed'), - subtitle: t('errorWithTransaction'), - }); - setSubmitting(false); - removePendingNonce(nonce); - } + const onFinalizedEvent = () => { + NotificationsController.emit({ + title: t('finalized'), + subtitle: t('transactionSuccessful'), + }); + setSubmitting(false); + removePendingNonce(nonce); }; - const resetTx = () => { - resetTxPayload(); - setTxSignature(null); + const onFailedTx = () => { + NotificationsController.emit({ + title: t('failed'), + subtitle: t('errorWithTransaction'), + }); setSubmitting(false); + removePendingNonce(nonce); }; - const resetManualTx = () => { - resetTx(); - handleResetLedgerTask(); + const resetTx = () => { + setSubmitting(false); }; const onError = (type?: string) => { @@ -215,80 +225,107 @@ export const useSubmitExtrinsic = ({ }); }; - const handleStatus = (status: AnyApi) => { - if (status.isReady) { + const handleStatus = (status: string) => { + if (status === 'broadcasted') { onReady(); } - if (status.isInBlock) { + if (status === 'txBestBlocksState') { onInBlock(); } }; - const unsubEvents = ['ExtrinsicSuccess', 'ExtrinsicFailed']; - // pre-submission state update setSubmitting(true); - const txPayloadValue = getTxPayload(); - const txSignature = getTxSignature(); - // handle signed transaction. - if (getTxSignature()) { - try { - txRef.current.addSignature( - fromRef.current, - txSignature, - txPayloadValue.toHex() - ); - - const unsub = await txRef.current.send( - ({ status, events = [] }: AnyApi) => { - if (!didTxReset.current) { - didTxReset.current = true; - resetManualTx(); - } - - handleStatus(status); - if (status.isFinalized) { - events.forEach(({ event: { method } }: AnyApi) => { - onFinalizedEvent(method); - if (unsubEvents?.includes(method)) { - unsub(); - } - }); - } - } - ); - } catch (e) { - onError(ManualSigners.includes(source) ? source : 'default'); + let signer: PolkadotSigner | undefined; + if (requiresManualSign(fromRef.current)) { + const pubKey = AccountId().enc(fromRef.current); + const networkInfo = { + decimals: units, + tokenSymbol: unit, + }; + + switch (source) { + case 'ledger': + signer = await new LedgerSigner( + pubKey, + getLedgerApp(network).txMetadataChainId + ).getPolkadotSigner(networkInfo, (account as LedgerAccount).index); + break; + + case 'vault': + signer = await new VaultSigner(pubKey, { + openPrompt: ( + onComplete: ( + status: VaultSignStatus, + result: VaultSignatureResult + ) => void, + toSign: Uint8Array + ) => { + openPromptWith( + , + 'small', + false + ); + }, + closePrompt: () => closePrompt(), + setSubmitting, + }).getPolkadotSigner(); + break; + + case 'wallet_connect': + signer = getPolkadotSignerFromPjs( + fromRef.current, + signWcTx, + // Signing bytes not currently being used. + // FIXME: Can implement, albeit won't be used. + async () => ({ + id: 0, + signature: '0x', + }) + ); + break; } } else { - // handle unsigned transaction. - const { signer } = account; - try { - const unsub = await txRef.current.signAndSend( - fromRef.current, - { signer, withSignedTransaction: true }, - ({ status, events = [] }: AnyApi) => { - if (!didTxReset.current) { - didTxReset.current = true; - resetTx(); - } - - handleStatus(status); - if (status.isFinalized) { - events.forEach(({ event: { method } }: AnyApi) => { - onFinalizedEvent(method); - if (unsubEvents?.includes(method)) { - unsub(); - } - }); - } + // Get the polkadot signer for this account. + signer = (await connectInjectedExtension(source)) + .getAccounts() + .find( + (a) => a.address === formatAccountSs58(fromRef.current, 42) + )?.polkadotSigner; + } + + try { + const sub = tx.signSubmitAndWatch(signer).subscribe({ + next: (result: { type: string }) => { + const eventType = result?.type; + + if (!didTxReset.current) { + didTxReset.current = true; + resetTx(); } - ); - } catch (e) { - onError('default'); - } + handleStatus(eventType); + if (eventType === 'finalized') { + onFinalizedEvent(); + sub?.unsubscribe(); + } + }, + error: (err: Error) => { + console.log(err); + if (err instanceof InvalidTxError) { + onFailedTx(); + } + sub?.unsubscribe(); + }, + }); + } catch (e) { + console.log(e); + onError('default'); } }; @@ -304,8 +341,6 @@ export const useSubmitExtrinsic = ({ setSender(fromRef.current); // re-calculate estimated tx fee. calculateEstimatedFee(); - // rebuild tx payload. - buildPayload(txRef.current, fromRef.current, uid); }, [tx?.toString(), tx?.method?.args?.calls?.toString(), from]); return { diff --git a/packages/app/src/hooks/useSubmitExtrinsic/types.ts b/packages/app/src/hooks/useSubmitExtrinsic/types.ts index 5eb2febf37..8284c34db1 100644 --- a/packages/app/src/hooks/useSubmitExtrinsic/types.ts +++ b/packages/app/src/hooks/useSubmitExtrinsic/types.ts @@ -18,3 +18,6 @@ export interface UseSubmitExtrinsic { proxySupported: boolean; submitAddress: MaybeAddress; } + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type UnsafeTx = AnyApi; diff --git a/packages/app/src/hooks/useUnstaking/index.tsx b/packages/app/src/hooks/useUnstaking/index.tsx index 6560493cfe..f089e4d6ca 100644 --- a/packages/app/src/hooks/useUnstaking/index.tsx +++ b/packages/app/src/hooks/useUnstaking/index.tsx @@ -28,7 +28,7 @@ export const useUnstaking = () => { // determine if user is fast unstaking. const inHead = head?.stashes.find((s: AnyJson) => s[0] === activeAccount) ?? undefined; - const inQueue = queueDeposit?.isGreaterThan(0) ?? false; + const inQueue = queueDeposit?.deposit?.isGreaterThan(0) ?? false; const registered = inHead || inQueue; diff --git a/packages/app/src/hooks/useValidatorFilters/index.tsx b/packages/app/src/hooks/useValidatorFilters/index.tsx index 90fbac48ea..879c62bac5 100644 --- a/packages/app/src/hooks/useValidatorFilters/index.tsx +++ b/packages/app/src/hooks/useValidatorFilters/index.tsx @@ -211,7 +211,7 @@ export const useValidatorFilters = () => { const filteredList: AnyFilter = []; for (const validator of list) { const identity = validatorIdentities[validator.address] ?? ''; - const identityRaw = identity?.info?.display?.Raw ?? ''; + const identityRaw = identity?.info?.display?.value?.asText() ?? ''; const identityAsBytes = u8aToString(u8aUnwrapBytes(identityRaw)); const identitySearch = ( identityAsBytes === '' ? identityRaw : identityAsBytes @@ -219,7 +219,7 @@ export const useValidatorFilters = () => { const superIdentity = validatorSupers[validator.address] ?? null; const superIdentityRaw = - superIdentity?.identity?.info?.display?.Raw ?? ''; + superIdentity?.identity?.info?.display?.value?.asText() ?? ''; const superIdentityAsBytes = u8aToString( u8aUnwrapBytes(superIdentityRaw) ); diff --git a/packages/app/src/library/Headers/Sync.tsx b/packages/app/src/library/Headers/Sync.tsx index e1eb216f6d..58dac03512 100644 --- a/packages/app/src/library/Headers/Sync.tsx +++ b/packages/app/src/library/Headers/Sync.tsx @@ -3,9 +3,7 @@ import { pageFromUri } from '@w3ux/utils'; import { usePayouts } from 'contexts/Payouts'; -import { usePlugins } from 'contexts/Plugins'; import { useBondedPools } from 'contexts/Pools/BondedPools'; -import { usePoolMembers } from 'contexts/Pools/PoolMembers'; import { useTxMeta } from 'contexts/TxMeta'; import { useValidators } from 'contexts/Validators/ValidatorEntries'; import { useSyncing } from 'hooks/useSyncing'; @@ -17,10 +15,8 @@ export const Sync = () => { const { pathname } = useLocation(); const { pendingNonces } = useTxMeta(); const { payoutsSynced } = usePayouts(); - const { pluginEnabled } = usePlugins(); const { validators } = useValidators(); const { bondedPools } = useBondedPools(); - const { poolMembersNode } = usePoolMembers(); // Keep syncing if on nominate page and still fetching payouts. const onNominateSyncing = () => { @@ -37,10 +33,7 @@ export const Sync = () => { // member sync if Subscan is enabled. const onPoolsSyncing = () => { if (pageFromUri(pathname, 'overview') === 'pools') { - if ( - !bondedPools.length || - (!poolMembersNode.length && !pluginEnabled('subscan')) - ) { + if (!bondedPools.length) { return true; } } diff --git a/packages/app/src/library/Prompt/index.tsx b/packages/app/src/library/Prompt/index.tsx index 256ffec570..ab68b535d5 100644 --- a/packages/app/src/library/Prompt/index.tsx +++ b/packages/app/src/library/Prompt/index.tsx @@ -5,7 +5,13 @@ import { usePrompt } from 'contexts/Prompt'; import { ContentWrapper, HeightWrapper, PromptWrapper } from './Wrappers'; export const Prompt = () => { - const { closePrompt, size, status, Prompt: PromptInner } = usePrompt(); + const { + size, + status, + closePrompt, + Prompt: PromptInner, + closeOnOutsideClick, + } = usePrompt(); if (status === 0) { return null; @@ -17,7 +23,15 @@ export const Prompt = () => { {PromptInner} - diff --git a/packages/app/src/library/QRCode/DisplayPayload.tsx b/packages/app/src/library/QRCode/DisplayPayload.tsx index 18b639be0c..c7e1fb4ecc 100644 --- a/packages/app/src/library/QRCode/DisplayPayload.tsx +++ b/packages/app/src/library/QRCode/DisplayPayload.tsx @@ -1,8 +1,8 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import { u8aConcat } from '@polkadot/util'; -import { decodeAddress } from '@polkadot/util-crypto'; +import { AccountId } from 'polkadot-api'; +import { mergeUint8 } from 'polkadot-api/utils'; import type { ReactElement } from 'react'; import { memo, useMemo } from 'react'; import { QrDisplay } from './Display.js'; @@ -13,19 +13,15 @@ const createSignPayload = ( cmd: number, payload: Uint8Array, genesisHash: Uint8Array -): Uint8Array => { - const SUBSTRATE_ID = new Uint8Array([0x53]); - const CRYPTO_SR25519 = new Uint8Array([0x01]); - - return u8aConcat( - SUBSTRATE_ID, - CRYPTO_SR25519, +): Uint8Array => + mergeUint8( + new Uint8Array([0x53]), // SUBSTRATE_ID + new Uint8Array([0x01]), // CRYPTO_SR25519 new Uint8Array([cmd]), - decodeAddress(address), + AccountId().enc(address), payload, genesisHash ); -}; const DisplayPayload = ({ address, diff --git a/packages/app/src/library/Signers/LedgerSigner.ts b/packages/app/src/library/Signers/LedgerSigner.ts new file mode 100644 index 0000000000..153b41de54 --- /dev/null +++ b/packages/app/src/library/Signers/LedgerSigner.ts @@ -0,0 +1,64 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import { merkleizeMetadata } from '@polkadot-api/merkleize-metadata'; +import { createV4Tx, getSignBytes } from '@polkadot-api/signers-common'; +import type { V15 } from '@polkadot-api/substrate-bindings'; +import { decAnyMetadata } from '@polkadot-api/substrate-bindings'; +import type { PolkadotSigner } from 'polkadot-api'; +import { mergeUint8 } from 'polkadot-api/utils'; +import { Ledger } from '../../contexts/LedgerHardware/static/ledger'; +import { getExtraSignedExtensions } from './util'; + +export class LedgerSigner { + #publicKey: Uint8Array; + #txMetadataChainId: string; + + constructor(pubKey: Uint8Array, txMetadataChainId: string) { + this.#publicKey = pubKey; + this.#txMetadataChainId = txMetadataChainId; + } + + async getPolkadotSigner( + networkInfo: { decimals: number; tokenSymbol: string }, + index: number + ): Promise { + const { app } = await Ledger.initialise(this.#txMetadataChainId); + + const signTx: PolkadotSigner['signTx'] = async ( + callData, + signedExtensions, + metadata + ) => { + const merkleizer = merkleizeMetadata(metadata, networkInfo); + const digest = merkleizer.digest(); + const v15 = decAnyMetadata(metadata).metadata.value as unknown as V15; + const { extra, additionalSigned } = getExtraSignedExtensions( + v15, + digest, + signedExtensions, + true + ); + + const toSign = mergeUint8(callData, ...extra, ...additionalSigned); + const { signature } = await Ledger.signPayload( + app, + index, + toSign, + merkleizer.getProofForExtrinsicPayload(toSign) + ); + return createV4Tx(v15, this.#publicKey, signature, extra, callData); + }; + + return { + publicKey: this.#publicKey, + signTx, + signBytes: getSignBytes(async (x) => { + const { signature } = await Ledger.signPayload(app, index, x); + // NOTE: the signature includes a "0x00" at the beginning, indicating a ed25519 signature. + // this is not needed for non-extrinsic signatures. + return signature.subarray(1); + }), + }; + } +} diff --git a/packages/app/src/library/Signers/VaultSigner/index.ts b/packages/app/src/library/Signers/VaultSigner/index.ts new file mode 100644 index 0000000000..7a4173a440 --- /dev/null +++ b/packages/app/src/library/Signers/VaultSigner/index.ts @@ -0,0 +1,99 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import { createV4Tx, getSignBytes } from '@polkadot-api/signers-common'; +import type { V15 } from '@polkadot-api/substrate-bindings'; +import { Binary, decAnyMetadata } from '@polkadot-api/substrate-bindings'; +import type { PolkadotSigner } from 'polkadot-api'; +import { mergeUint8 } from 'polkadot-api/utils'; +import type { + VaultPromptHandlers, + VaultSignatureResult, + VaultSignStatus, +} from './types'; + +export class VaultSigner { + #publicKey: Uint8Array; + #promptHandlers: VaultPromptHandlers; + + constructor(pubKey: Uint8Array, promptHandlers: VaultPromptHandlers) { + this.#publicKey = pubKey; + this.#promptHandlers = promptHandlers; + } + + #showPrompt = (toSign: Uint8Array): Promise => + new Promise((resolve) => { + const handleComplete = ( + status: VaultSignStatus, + result: VaultSignatureResult + ) => { + this.#promptHandlers.closePrompt(); + if (status === 'cancelled') { + this.#promptHandlers.setSubmitting(false); + resolve(null); + } else { + resolve(result); + } + }; + this.#promptHandlers.openPrompt((status, signature) => { + handleComplete(status, signature); + }, toSign); + }); + + async getPolkadotSigner(): Promise { + const signTx: PolkadotSigner['signTx'] = async ( + callData, + signedExtensions, + metadata + ) => { + const v15 = decAnyMetadata(metadata).metadata.value as unknown as V15; + const extra: Uint8Array[] = []; + const additionalSigned: Uint8Array[] = []; + v15.extrinsic.signedExtensions.map(({ identifier }) => { + const signedExtension = signedExtensions[identifier]; + if (!signedExtension) { + throw new Error(`Missing ${identifier} signed extension`); + } + extra.push(signedExtension.value); + additionalSigned.push(signedExtension.additionalSigned); + }); + + // The byte length is required as a prefix here. + const prefix = new Uint8Array([callData.length * 4]); + const toSign = mergeUint8( + prefix, + callData, + ...extra, + ...additionalSigned + ); + + // Start flow to sign QR Code here. + const signature = await this.#showPrompt(toSign); + + if (signature === null) { + throw 'Invalid signature'; + } + return createV4Tx( + v15, + this.#publicKey, + Binary.fromHex(signature).asBytes(), + extra, + callData + ); + }; + + return { + publicKey: this.#publicKey, + signTx, + signBytes: getSignBytes(async (x) => { + const signatureHex = await this.#showPrompt(x); + if (!signatureHex) { + throw 'Invalid signature'; + } + // NOTE: the signature includes a "0x00" at the beginning, indicating a ed25519 signature. + // this is not needed for non-extrinsic signatures. + return Binary.fromHex(signatureHex).asBytes().subarray(1); + }), + }; + } +} diff --git a/packages/app/src/library/Signers/VaultSigner/types.ts b/packages/app/src/library/Signers/VaultSigner/types.ts new file mode 100644 index 0000000000..e84578a963 --- /dev/null +++ b/packages/app/src/library/Signers/VaultSigner/types.ts @@ -0,0 +1,18 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +export interface VaultPromptHandlers { + openPrompt: ( + onComplete: ( + status: 'complete' | 'cancelled', + result: `0x${string}` | null + ) => void, + toSign: Uint8Array + ) => void; + closePrompt: () => void; + setSubmitting: (submitting: boolean) => void; +} + +export type VaultSignStatus = 'complete' | 'cancelled'; + +export type VaultSignatureResult = `0x${string}` | null; diff --git a/packages/app/src/library/Signers/util.ts b/packages/app/src/library/Signers/util.ts new file mode 100644 index 0000000000..f1ff80394d --- /dev/null +++ b/packages/app/src/library/Signers/util.ts @@ -0,0 +1,43 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { V15 } from '@polkadot-api/substrate-bindings'; +import { mergeUint8 } from 'polkadot-api/utils'; + +const CheckMetadataHash = 'CheckMetadataHash'; + +export const getExtraSignedExtensions = ( + v15: V15, + digest: Uint8Array, + signedExtensions: Record< + string, + { + identifier: string; + value: Uint8Array; + additionalSigned: Uint8Array; + } + >, + checkMetadataHash = false +): { + extra: Uint8Array[]; + additionalSigned: Uint8Array[]; +} => { + // NOTE: Assuming `CheckMetadataHash` signed extension exists in metadata. Could introduce + // error here for `useSubmitExtrinsic` to handle. + const extra: Uint8Array[] = []; + const additionalSigned: Uint8Array[] = []; + v15.extrinsic.signedExtensions.map(({ identifier }) => { + if (checkMetadataHash && identifier === CheckMetadataHash) { + extra.push(Uint8Array.from([1])); + additionalSigned.push(mergeUint8(Uint8Array.from([1]), digest)); + return; + } + const signedExtension = signedExtensions[identifier]; + if (signedExtension) { + extra.push(signedExtension.value); + additionalSigned.push(signedExtension.additionalSigned); + } + }); + + return { extra, additionalSigned }; +}; diff --git a/packages/app/src/library/SubmitTx/ManualSign/Ledger/Submit.tsx b/packages/app/src/library/SubmitTx/ManualSign/Ledger/Submit.tsx index 1c004dd47d..d9905d44fa 100644 --- a/packages/app/src/library/SubmitTx/ManualSign/Ledger/Submit.tsx +++ b/packages/app/src/library/SubmitTx/ManualSign/Ledger/Submit.tsx @@ -3,9 +3,6 @@ import { faUsb } from '@fortawesome/free-brands-svg-icons'; import { faSquarePen } from '@fortawesome/free-solid-svg-icons'; -import type { LedgerAccount } from '@w3ux/react-connect-kit/types'; -import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { useLedgerHardware } from 'contexts/LedgerHardware'; import { getLedgerApp } from 'contexts/LedgerHardware/Utils'; import { useNetwork } from 'contexts/Network'; @@ -23,37 +20,11 @@ export const Submit = ({ disabled, }: LedgerSubmitProps) => { const { t } = useTranslation('library'); - const { - handleSignTx, - getIsExecuting, - integrityChecked, - checkRuntimeVersion, - } = useLedgerHardware(); const { network } = useNetwork(); const { getTxSignature } = useTxMeta(); - const { getAccount } = useImportedAccounts(); - const { activeAccount } = useActiveAccounts(); - const { getTxMetadata, getTxPayload, getPayloadUid } = useTxMeta(); const { txMetadataChainId } = getLedgerApp(network); - - const getAddressIndex = () => - (getAccount(activeAccount) as LedgerAccount)?.index || 0; - - // Handle transaction submission - const handleTxSubmit = async () => { - const uid = getPayloadUid(); - const accountIndex = getAddressIndex(); - const txMetadata = await getTxMetadata(); - const payload = await getTxPayload(); - - await handleSignTx( - txMetadataChainId, - uid, - accountIndex, - payload, - txMetadata - ); - }; + const { getIsExecuting, integrityChecked, checkRuntimeVersion } = + useLedgerHardware(); // Check device runtime version. const handleCheckRuntimeVersion = async () => { @@ -66,9 +37,7 @@ export const Submit = ({ // Button `onClick` handler depends whether integrityChecked and whether tx has been submitted. const handleOnClick = !integrityChecked ? handleCheckRuntimeVersion - : txReady - ? onSubmit - : handleTxSubmit; + : onSubmit; // Determine button text. const text = !integrityChecked diff --git a/packages/app/src/library/SubmitTx/ManualSign/Vault/SignPrompt.tsx b/packages/app/src/library/SubmitTx/ManualSign/Vault/SignPrompt.tsx index f169647bdb..e5ae9fbfb0 100644 --- a/packages/app/src/library/SubmitTx/ManualSign/Vault/SignPrompt.tsx +++ b/packages/app/src/library/SubmitTx/ManualSign/Vault/SignPrompt.tsx @@ -6,9 +6,8 @@ import { faChevronRight, } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import type { AnyJson } from '@w3ux/types'; -import { usePrompt } from 'contexts/Prompt'; -import { useTxMeta } from 'contexts/TxMeta'; +import { Bytes } from '@polkadot-api/substrate-bindings'; +import { useApi } from 'contexts/Api'; import { QRViewerWrapper } from 'library/Import/Wrappers'; import { QrDisplayPayload } from 'library/QRCode/DisplayPayload'; import { QrScanSignature } from 'library/QRCode/ScanSignature'; @@ -17,13 +16,15 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ButtonPrimary, ButtonSecondary } from 'ui-buttons'; -export const SignPrompt = ({ submitAddress }: SignerPromptProps) => { +export const SignPrompt = ({ + submitAddress, + toSign, + onComplete, +}: SignerPromptProps) => { + const { + chainSpecs: { genesisHash }, + } = useApi(); const { t } = useTranslation('library'); - const { getTxPayload, setTxSignature } = useTxMeta(); - - const payload = getTxPayload(); - const payloadU8a = payload?.toU8a(); - const { closePrompt } = usePrompt(); // Whether user is on sign or submit stage. const [stage, setStage] = useState(1); @@ -47,8 +48,8 @@ export const SignPrompt = ({ submitAddress }: SignerPromptProps) => { @@ -57,9 +58,8 @@ export const SignPrompt = ({ submitAddress }: SignerPromptProps) => {
{ - closePrompt(); - setTxSignature(signature); + onScan={({ signature }) => { + onComplete('complete', signature); }} />
@@ -90,7 +90,9 @@ export const SignPrompt = ({ submitAddress }: SignerPromptProps) => { text={t('cancel')} lg marginLeft - onClick={() => closePrompt()} + onClick={() => { + onComplete('cancelled', null); + }} /> diff --git a/packages/app/src/library/SubmitTx/ManualSign/Vault/index.tsx b/packages/app/src/library/SubmitTx/ManualSign/Vault/index.tsx index 211d7ff918..b8a5364600 100644 --- a/packages/app/src/library/SubmitTx/ManualSign/Vault/index.tsx +++ b/packages/app/src/library/SubmitTx/ManualSign/Vault/index.tsx @@ -12,7 +12,6 @@ import type { ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; import { ButtonSubmit } from 'ui-buttons'; import type { SubmitProps } from '../../types'; -import { SignPrompt } from './SignPrompt'; export const Vault = ({ onSubmit, @@ -24,9 +23,9 @@ export const Vault = ({ displayFor, }: SubmitProps & { buttons?: ReactNode[] }) => { const { t } = useTranslation('library'); + const { txFeesValid } = useTxMeta(); + const { status: promptStatus } = usePrompt(); const { accountHasSigner } = useImportedAccounts(); - const { txFeesValid, getTxSignature } = useTxMeta(); - const { openPromptWith, status: promptStatus } = usePrompt(); // The state under which submission is disabled. const disabled = @@ -34,20 +33,15 @@ export const Vault = ({ // Format submit button based on whether signature currently exists or submission is ongoing. let buttonText: string; - let buttonOnClick: () => void; let buttonDisabled: boolean; let buttonPulse: boolean; - if (getTxSignature() !== null || submitting) { + if (submitting) { buttonText = submitText || ''; - buttonOnClick = onSubmit; buttonDisabled = disabled; buttonPulse = !(!valid || promptStatus !== 0); } else { - buttonText = promptStatus === 0 ? t('sign') : t('signing'); - buttonOnClick = async () => { - openPromptWith(, 'small'); - }; + buttonText = t('sign'); buttonDisabled = disabled || promptStatus !== 0; buttonPulse = !disabled || promptStatus === 0; } @@ -67,14 +61,14 @@ export const Vault = ({ text={buttonText} iconLeft={faSquarePen} iconTransform="grow-2" - onClick={() => buttonOnClick()} + onClick={() => onSubmit()} pulse={buttonPulse} /> ) : ( diff --git a/packages/app/src/library/SubmitTx/ManualSign/WalletConnect/index.tsx b/packages/app/src/library/SubmitTx/ManualSign/WalletConnect/index.tsx index eb236a8b39..5277f98e00 100644 --- a/packages/app/src/library/SubmitTx/ManualSign/WalletConnect/index.tsx +++ b/packages/app/src/library/SubmitTx/ManualSign/WalletConnect/index.tsx @@ -3,16 +3,15 @@ import { faSquarePen } from '@fortawesome/free-solid-svg-icons'; import { appendOrEmpty } from '@w3ux/utils'; -import { useApi } from 'contexts/Api'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { useTxMeta } from 'contexts/TxMeta'; import { useWalletConnect } from 'contexts/WalletConnect'; import { EstimatedTxFee } from 'library/EstimatedTxFee'; import { ButtonSubmitLarge } from 'library/SubmitTx/ButtonSubmitLarge'; -import { useState, type ReactNode } from 'react'; +import type { SubmitProps } from 'library/SubmitTx/types'; +import { type ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; import { ButtonSubmit } from 'ui-buttons'; -import type { SubmitProps } from '../../types'; export const WalletConnect = ({ onSubmit, @@ -24,72 +23,43 @@ export const WalletConnect = ({ displayFor, }: SubmitProps & { buttons?: ReactNode[] }) => { const { t } = useTranslation('library'); - const { api } = useApi(); + const { txFeesValid, sender } = useTxMeta(); const { accountHasSigner } = useImportedAccounts(); - const { wcSessionActive, connectProvider, fetchAddresses, signWcTx } = + const { wcSessionActive, connectProvider, fetchAddresses } = useWalletConnect(); - const { txFeesValid, sender, getTxPayloadJson, setTxSignature } = useTxMeta(); - - // Store whether the user is currently signing a transaction. - const [isSgning, setIsSigning] = useState(false); // The state under which submission is disabled. - const disabled = - submitting || !valid || !accountHasSigner(submitAddress) || !txFeesValid; - const alreadySubmitted = submitting; + const disabled = !valid || !accountHasSigner(submitAddress) || !txFeesValid; // Format submit button based on whether signature currently exists or submission is ongoing. let buttonOnClick: () => void; let buttonDisabled: boolean; let buttonPulse: boolean; - if (alreadySubmitted) { - buttonOnClick = onSubmit; + const connectAndSubmit = async () => { + // If Wallet Connect session is not active, re-connect. + if (!wcSessionActive) { + await connectProvider(); + } + const wcAccounts = await fetchAddresses(); + const accountExists = sender && wcAccounts.includes(sender); + if (!sender || !accountExists) { + return; + } + onSubmit(); + }; + + if (submitting) { + buttonOnClick = connectAndSubmit; buttonDisabled = disabled; - buttonPulse = valid; + buttonPulse = false; } else { - buttonOnClick = async () => { - if (!api) { - return; - } - - // If Wallet Connect session is not active, re-connect. - if (!wcSessionActive) { - await connectProvider(); - } - - const wcAccounts = await fetchAddresses(); - const accountExists = sender && wcAccounts.includes(sender); - - const payload = getTxPayloadJson(); - if (!sender || !payload || !accountExists) { - return; - } - - setIsSigning(true); - - const caip = `polkadot:${api.genesisHash.toHex().substring(2).substring(0, 32)}`; - - try { - const signature = await signWcTx(caip, payload, sender); - if (signature) { - setTxSignature(signature); - } - } catch (e) { - setIsSigning(false); - } - setIsSigning(false); - }; - + buttonOnClick = connectAndSubmit; buttonDisabled = disabled; buttonPulse = !disabled; } - const buttonText = alreadySubmitted - ? submitText || '' - : isSgning - ? t('signing') - : t('sign'); + const buttonText = submitting ? submitText || '' : t('sign'); return (
diff --git a/packages/app/src/library/SubmitTx/types.ts b/packages/app/src/library/SubmitTx/types.ts index 353c7f5f15..4fdcd0f0d6 100644 --- a/packages/app/src/library/SubmitTx/types.ts +++ b/packages/app/src/library/SubmitTx/types.ts @@ -3,6 +3,7 @@ import type { IconProp } from '@fortawesome/fontawesome-svg-core'; import type { DisplayFor } from '@w3ux/types'; +import type { VaultSignatureResult } from 'library/Signers/VaultSigner/types'; import type { ReactNode } from 'react'; import type { MaybeAddress } from 'types'; @@ -26,6 +27,11 @@ export interface SubmitProps { export interface SignerPromptProps { submitAddress: MaybeAddress; + toSign: Uint8Array; + onComplete: ( + status: 'complete' | 'cancelled', + signature: VaultSignatureResult + ) => void; } export interface LedgerSubmitProps { diff --git a/packages/app/src/library/Utils/index.ts b/packages/app/src/library/Utils/index.ts index a04962fbb6..2765f3c266 100644 --- a/packages/app/src/library/Utils/index.ts +++ b/packages/app/src/library/Utils/index.ts @@ -78,3 +78,13 @@ export const timeleftAsString = ( } return str; }; + +// Convert a perbill BigNumber value into a percentage. +export const perbillToPercent = ( + value: BigNumber | bigint | number +): BigNumber => { + if (typeof value === 'bigint' || typeof value === 'number') { + value = new BigNumber(value.toString()); + } + return value.dividedBy('10000000'); +}; diff --git a/packages/app/src/library/ValidatorList/ValidatorItem/Utils.tsx b/packages/app/src/library/ValidatorList/ValidatorItem/Utils.tsx index dfaa000e00..36171cf71c 100644 --- a/packages/app/src/library/ValidatorList/ValidatorItem/Utils.tsx +++ b/packages/app/src/library/ValidatorList/ValidatorItem/Utils.tsx @@ -17,7 +17,7 @@ export const getIdentityDisplay = ( const superIdentity = _superIdentity?.identity ?? null; const superRaw = _superIdentity?.superOf?.[1]?.Raw ?? null; - const superDisplay = superIdentity?.info?.display?.Raw ?? null; + const superDisplay = superIdentity?.info?.display?.value?.asText() ?? null; // check if super raw has been encoded const superRawAsBytes = u8aToString(u8aUnwrapBytes(superRaw)); @@ -35,7 +35,7 @@ export const getIdentityDisplay = ( if (!foundSuper) { // cehck sub identity exists, get display.Raw if it does - const identity = _identity?.info?.display?.Raw ?? null; + const identity = _identity?.info?.display?.value?.asText() ?? null; // check if identity has been byte encoded const subIdentityAsBytes = u8aToString(u8aUnwrapBytes(identity)); diff --git a/packages/app/src/modals/BalanceTest/index.tsx b/packages/app/src/modals/BalanceTest/index.tsx index e9f92f7191..2ef520ea41 100644 --- a/packages/app/src/modals/BalanceTest/index.tsx +++ b/packages/app/src/modals/BalanceTest/index.tsx @@ -3,9 +3,9 @@ import { unitToPlanck } from '@w3ux/utils'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useApi } from 'contexts/Api'; import { useNetwork } from 'contexts/Network'; import { useTxMeta } from 'contexts/TxMeta'; +import { ApiController } from 'controllers/Api'; import { useBatchCall } from 'hooks/useBatchCall'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { useOverlay } from 'kits/Overlay/Provider'; @@ -15,38 +15,41 @@ import { SubmitTx } from 'library/SubmitTx'; import { useEffect } from 'react'; export const BalanceTest = () => { - const { api } = useApi(); const { + network, networkData: { units }, } = useNetwork(); - const { activeAccount } = useActiveAccounts(); const { notEnoughFunds } = useTxMeta(); const { newBatchCall } = useBatchCall(); - const { setModalStatus, setModalResize } = useOverlay().modal; + const { activeAccount } = useActiveAccounts(); + const { setModalResize, setModalStatus } = useOverlay().modal; // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); + const tx = null; - if (!api || !activeAccount) { + if (!pApi || !activeAccount) { return tx; } const txs = [ - api.tx.balances.transfer( - { - id: '1554u1a67ApEt5xmjbZwjgDNaVckbzB6cjRHWAQ1SpNkNxTd', + pApi.tx.Balances.transfer_keep_alive({ + dest: { + type: 'Id', + value: '1554u1a67ApEt5xmjbZwjgDNaVckbzB6cjRHWAQ1SpNkNxTd', }, - unitToPlanck('0.1', units).toString() - ), - api.tx.balances.transfer( - { - id: '1554u1a67ApEt5xmjbZwjgDNaVckbzB6cjRHWAQ1SpNkNxTd', + value: BigInt(unitToPlanck('0.1', units).toString()), + }), + pApi.tx.Balances.transfer_keep_alive({ + dest: { + type: 'Id', + value: '1554u1a67ApEt5xmjbZwjgDNaVckbzB6cjRHWAQ1SpNkNxTd', }, - unitToPlanck('0.05', units).toString() - ), + value: BigInt(unitToPlanck('0.1', units).toString()), + }), ]; const batch = newBatchCall(txs, activeAccount); - return batch; }; diff --git a/packages/app/src/modals/Bond/index.tsx b/packages/app/src/modals/Bond/index.tsx index 3d5f61e91a..2e62a33c7c 100644 --- a/packages/app/src/modals/Bond/index.tsx +++ b/packages/app/src/modals/Bond/index.tsx @@ -4,11 +4,11 @@ import { unitToPlanck } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useApi } from 'contexts/Api'; import { useNetwork } from 'contexts/Network'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { useTransferOptions } from 'contexts/TransferOptions'; import { useTxMeta } from 'contexts/TxMeta'; +import { ApiController } from 'controllers/Api'; import { useBondGreatestFee } from 'hooks/useBondGreatestFee'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; @@ -25,8 +25,8 @@ import { useTranslation } from 'react-i18next'; export const Bond = () => { const { t } = useTranslation('modals'); - const { api } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); const { notEnoughFunds } = useTxMeta(); @@ -95,8 +95,9 @@ export const Bond = () => { // determine whether this is a pool or staking transaction. const determineTx = (bondToSubmit: BigNumber) => { + const { pApi } = ApiController.get(network); let tx = null; - if (!api) { + if (!pApi) { return tx; } @@ -107,18 +108,21 @@ export const Bond = () => { : bondToSubmit.toString(); if (isPooling) { - tx = api.tx.nominationPools.bondExtra({ - FreeBalance: bondAsString, + tx = pApi.tx.NominationPools.bond_extra({ + extra: { + type: 'FreeBalance', + value: BigInt(bondAsString), + }, }); } else if (isStaking) { - tx = api.tx.staking.bondExtra(bondAsString); + tx = pApi.tx.Staking.bond_extra({ max_additional: BigInt(bondAsString) }); } return tx; }; // the actual bond tx to submit const getTx = (bondToSubmit: BigNumber) => { - if (!api || !activeAccount) { + if (!activeAccount) { return null; } return determineTx(bondToSubmit); diff --git a/packages/app/src/modals/ChangePoolRoles/index.tsx b/packages/app/src/modals/ChangePoolRoles/index.tsx index e69b45efcc..f9d078b9bc 100644 --- a/packages/app/src/modals/ChangePoolRoles/index.tsx +++ b/packages/app/src/modals/ChangePoolRoles/index.tsx @@ -2,9 +2,10 @@ // SPDX-License-Identifier: GPL-3.0-only import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useApi } from 'contexts/Api'; +import { useNetwork } from 'contexts/Network'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { useTxMeta } from 'contexts/TxMeta'; +import { ApiController } from 'controllers/Api'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { useOverlay } from 'kits/Overlay/Provider'; import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; @@ -17,10 +18,10 @@ import { Wrapper } from './Wrapper'; export const ChangePoolRoles = () => { const { t } = useTranslation('modals'); - const { api } = useApi(); - const { activeAccount } = useActiveAccounts(); + const { network } = useNetwork(); const { notEnoughFunds } = useTxMeta(); const { replacePoolRoles } = useBondedPools(); + const { activeAccount } = useActiveAccounts(); const { setModalStatus, config: { options }, @@ -30,18 +31,26 @@ export const ChangePoolRoles = () => { // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; - const root = roleEdits?.root?.newAddress - ? { Set: roleEdits?.root?.newAddress } - : 'Remove'; - const nominator = roleEdits?.nominator?.newAddress - ? { Set: roleEdits?.nominator?.newAddress } - : 'Remove'; - const bouncer = roleEdits?.bouncer?.newAddress - ? { Set: roleEdits?.bouncer?.newAddress } - : 'Remove'; + if (!pApi) { + return tx; + } + + tx = pApi.tx.NominationPools.update_roles({ + pool_id: poolId, + new_root: roleEdits?.root?.newAddress + ? { type: 'Set', value: roleEdits.root.newAddress } + : { type: 'Remove', value: undefined }, + + new_nominator: roleEdits?.nominator?.newAddress + ? { type: 'Set', value: roleEdits.nominator.newAddress } + : { type: 'Remove', value: undefined }, - tx = api?.tx.nominationPools?.updateRoles(poolId, root, nominator, bouncer); + new_bouncer: roleEdits?.bouncer?.newAddress + ? { type: 'Set', value: roleEdits.bouncer.newAddress } + : { type: 'Remove', value: undefined }, + }); return tx; }; diff --git a/packages/app/src/modals/ClaimPayouts/Forms.tsx b/packages/app/src/modals/ClaimPayouts/Forms.tsx index 21ed87ff5e..86d3cb7cb2 100644 --- a/packages/app/src/modals/ClaimPayouts/Forms.tsx +++ b/packages/app/src/modals/ClaimPayouts/Forms.tsx @@ -5,9 +5,9 @@ import { faChevronLeft } from '@fortawesome/free-solid-svg-icons'; import { planckToUnit } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useApi } from 'contexts/Api'; import { useNetwork } from 'contexts/Network'; import { usePayouts } from 'contexts/Payouts'; +import { ApiController } from 'controllers/Api'; import { SubscanController } from 'controllers/Subscan'; import { useBatchCall } from 'hooks/useBatchCall'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; @@ -32,8 +32,8 @@ export const Forms = forwardRef( ref: ForwardedRef ) => { const { t } = useTranslation('modals'); - const { api } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); const { newBatchCall } = useBatchCall(); @@ -59,7 +59,8 @@ export const Forms = forwardRef( ) || 0; const getCalls = () => { - if (!api) { + const { pApi } = ApiController.get(network); + if (!pApi) { return []; } @@ -69,7 +70,13 @@ export const Forms = forwardRef( return []; } return paginatedValidators.forEach(([page, v]) => - calls.push(api.tx.staking.payoutStakersByPage(v, era, page)) + calls.push( + pApi.tx.Staking.payout_stakers_by_page({ + validator_stash: v, + era: Number(era), + page, + }) + ) ); }); return calls; @@ -89,7 +96,7 @@ export const Forms = forwardRef( const getTx = () => { const tx = null; const calls = getCalls(); - if (!valid || !api || !calls.length) { + if (!valid || !calls.length) { return tx; } diff --git a/packages/app/src/modals/ClaimReward/index.tsx b/packages/app/src/modals/ClaimReward/index.tsx index 32e5f7e22b..f0e8b8d6ed 100644 --- a/packages/app/src/modals/ClaimReward/index.tsx +++ b/packages/app/src/modals/ClaimReward/index.tsx @@ -3,10 +3,10 @@ import { planckToUnit } from '@w3ux/utils'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useApi } from 'contexts/Api'; import { useNetwork } from 'contexts/Network'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { useTxMeta } from 'contexts/TxMeta'; +import { ApiController } from 'controllers/Api'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { useOverlay } from 'kits/Overlay/Provider'; @@ -21,20 +21,19 @@ import { useTranslation } from 'react-i18next'; export const ClaimReward = () => { const { t } = useTranslation('modals'); - const { api } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); - const { notEnoughFunds } = useTxMeta(); - const { activeAccount } = useActiveAccounts(); - const { getSignerWarnings } = useSignerWarnings(); - const { activePool, pendingPoolRewards } = useActivePool(); const { setModalStatus, config: { options }, setModalResize, } = useOverlay().modal; - + const { notEnoughFunds } = useTxMeta(); + const { activeAccount } = useActiveAccounts(); + const { getSignerWarnings } = useSignerWarnings(); + const { activePool, pendingPoolRewards } = useActivePool(); const { claimType } = options; // ensure selected payout is valid @@ -51,15 +50,18 @@ export const ClaimReward = () => { // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; - if (!api) { + if (!pApi) { return tx; } if (claimType === 'bond') { - tx = api.tx.nominationPools.bondExtra('Rewards'); + tx = pApi.tx.NominationPools.bond_extra({ + extra: { type: 'Rewards', value: undefined }, + }); } else { - tx = api.tx.nominationPools.claimPayout(); + tx = pApi.tx.NominationPools.claim_payout(); } return tx; }; diff --git a/packages/app/src/modals/ImportWalletConnect/index.tsx b/packages/app/src/modals/ImportWalletConnect/index.tsx index b5a2dc0375..2386a9a6c9 100644 --- a/packages/app/src/modals/ImportWalletConnect/index.tsx +++ b/packages/app/src/modals/ImportWalletConnect/index.tsx @@ -25,7 +25,7 @@ export const ImportWalletConnect = () => { const { t } = useTranslation(); const { addWcAccount, getWcAccounts, wcAccountExists, renameWcAccount } = useWcAccounts(); - const { api } = useApi(); + const { isReady } = useApi(); const { network } = useNetwork(); const { status: promptStatus } = usePrompt(); const { replaceModal, setModalResize } = useOverlay().modal; @@ -41,7 +41,7 @@ export const ImportWalletConnect = () => { // Handle wallet account importing. const handleImportAddresses = async () => { - if (!wcInitialized || !api) { + if (!wcInitialized || !isReady) { return; } diff --git a/packages/app/src/modals/ManageFastUnstake/index.tsx b/packages/app/src/modals/ManageFastUnstake/index.tsx index 0e76b3dcac..5bb9bdf833 100644 --- a/packages/app/src/modals/ManageFastUnstake/index.tsx +++ b/packages/app/src/modals/ManageFastUnstake/index.tsx @@ -9,6 +9,7 @@ import { useFastUnstake } from 'contexts/FastUnstake'; import { useNetwork } from 'contexts/Network'; import { useTransferOptions } from 'contexts/TransferOptions'; import { useTxMeta } from 'contexts/TxMeta'; +import { ApiController } from 'controllers/Api'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { useUnstaking } from 'hooks/useUnstaking'; @@ -27,18 +28,18 @@ import { useTranslation } from 'react-i18next'; export const ManageFastUnstake = () => { const { t } = useTranslation('modals'); const { - api, consts: { bondDuration, fastUnstakeDeposit }, networkMetrics: { fastUnstakeErasToCheckPerBlock }, activeEra, } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); - const { activeAccount } = useActiveAccounts(); const { notEnoughFunds } = useTxMeta(); const { getBondedAccount } = useBonded(); const { isFastUnstaking } = useUnstaking(); + const { activeAccount } = useActiveAccounts(); const { setModalResize, setModalStatus } = useOverlay().modal; const { getSignerWarnings } = useSignerWarnings(); const { feeReserve, getTransferOptions } = useTransferOptions(); @@ -82,14 +83,15 @@ export const ManageFastUnstake = () => { // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; - if (!valid || !api) { + if (!valid || !pApi) { return tx; } if (!isFastUnstaking) { - tx = api.tx.fastUnstake.registerFastUnstake(); + tx = pApi.tx.FastUnstake.register_fast_unstake(); } else { - tx = api.tx.fastUnstake.deregister(); + tx = pApi.tx.FastUnstake.deregister(); } return tx; }; @@ -179,7 +181,7 @@ export const ManageFastUnstake = () => { {t('fastUnstakeOnceRegistered')}

- {t('fastUnstakeCurrentQueue')}: {counterForQueue} + {t('fastUnstakeCurrentQueue')}: {counterForQueue || 0}

@@ -188,7 +190,7 @@ export const ManageFastUnstake = () => {

- {t('fastUnstakeCurrentQueue')}: {counterForQueue} + {t('fastUnstakeCurrentQueue')}: {counterForQueue || 0}

{t('fastUnstakeUnorderedNote')}

diff --git a/packages/app/src/modals/ManagePool/Forms/ClaimCommission/index.tsx b/packages/app/src/modals/ManagePool/Forms/ClaimCommission/index.tsx index 808be751f1..7dddef3106 100644 --- a/packages/app/src/modals/ManagePool/Forms/ClaimCommission/index.tsx +++ b/packages/app/src/modals/ManagePool/Forms/ClaimCommission/index.tsx @@ -5,9 +5,9 @@ import { faChevronLeft } from '@fortawesome/free-solid-svg-icons'; import { rmCommas } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useApi } from 'contexts/Api'; import { useNetwork } from 'contexts/Network'; import { useActivePool } from 'contexts/Pools/ActivePool'; +import { ApiController } from 'controllers/Api'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { useOverlay } from 'kits/Overlay/Provider'; @@ -29,8 +29,8 @@ export const ClaimCommission = ({ setSection: Dispatch>; }) => { const { t } = useTranslation('modals'); - const { api } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); const { setModalStatus } = useOverlay().modal; @@ -52,10 +52,11 @@ export const ClaimCommission = ({ // tx to submit const getTx = () => { - if (!valid || !api) { + const { pApi } = ApiController.get(network); + if (!valid || !pApi || poolId === undefined) { return null; } - return api.tx.nominationPools.claimCommission(poolId); + return pApi.tx.NominationPools.claim_commission({ pool_id: poolId }); }; const submitExtrinsic = useSubmitExtrinsic({ diff --git a/packages/app/src/modals/ManagePool/Forms/LeavePool/index.tsx b/packages/app/src/modals/ManagePool/Forms/LeavePool/index.tsx index ac44a3e3e8..20d1d7c920 100644 --- a/packages/app/src/modals/ManagePool/Forms/LeavePool/index.tsx +++ b/packages/app/src/modals/ManagePool/Forms/LeavePool/index.tsx @@ -8,6 +8,7 @@ import { useApi } from 'contexts/Api'; import { useNetwork } from 'contexts/Network'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { useTransferOptions } from 'contexts/TransferOptions'; +import { ApiController } from 'controllers/Api'; import { getUnixTime } from 'date-fns'; import { useErasToTimeLeft } from 'hooks/useErasToTimeLeft'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; @@ -20,8 +21,7 @@ import { Warning } from 'library/Form/Warning'; import { SubmitTx } from 'library/SubmitTx'; import { planckToUnitBn, timeleftAsString } from 'library/Utils'; import { StaticNote } from 'modals/Utils/StaticNote'; -import type { Dispatch, SetStateAction } from 'react'; -import { useEffect, useState } from 'react'; +import { useEffect, useState, type Dispatch, type SetStateAction } from 'react'; import { useTranslation } from 'react-i18next'; import { ButtonSubmitInvert } from 'ui-buttons'; @@ -31,8 +31,9 @@ export const LeavePool = ({ setSection: Dispatch>; }) => { const { t } = useTranslation('modals'); - const { api, consts } = useApi(); + const { consts } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); const { erasToSeconds } = useErasToTimeLeft(); @@ -80,8 +81,9 @@ export const LeavePool = ({ // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; - if (!api || !activeAccount) { + if (!pApi || !activeAccount) { return tx; } @@ -90,7 +92,13 @@ export const LeavePool = ({ units ).toString(); - tx = api.tx.nominationPools.unbond(activeAccount, bondToSubmit); + tx = pApi.tx.NominationPools.unbond({ + member_account: { + type: 'Id', + value: activeAccount, + }, + unbonding_points: BigInt(bondToSubmit), + }); return tx; }; diff --git a/packages/app/src/modals/ManagePool/Forms/ManageCommission/index.tsx b/packages/app/src/modals/ManagePool/Forms/ManageCommission/index.tsx index 41a7daa3a0..5c23ae5223 100644 --- a/packages/app/src/modals/ManagePool/Forms/ManageCommission/index.tsx +++ b/packages/app/src/modals/ManagePool/Forms/ManageCommission/index.tsx @@ -6,8 +6,10 @@ import BigNumber from 'bignumber.js'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useApi } from 'contexts/Api'; import { useHelp } from 'contexts/Help'; +import { useNetwork } from 'contexts/Network'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { useBondedPools } from 'contexts/Pools/BondedPools'; +import { ApiController } from 'controllers/Api'; import { useBatchCall } from 'hooks/useBatchCall'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; @@ -18,8 +20,7 @@ import { ActionItem } from 'library/ActionItem'; import { Warning } from 'library/Form/Warning'; import { SubmitTx } from 'library/SubmitTx'; import 'rc-slider/assets/index.css'; -import type { Dispatch, SetStateAction } from 'react'; -import { useEffect, useState } from 'react'; +import { useEffect, useState, type Dispatch, type SetStateAction } from 'react'; import { useTranslation } from 'react-i18next'; import { ButtonHelp, ButtonSubmitInvert } from 'ui-buttons'; import { ChangeRate } from './ChangeRate'; @@ -37,15 +38,8 @@ export const ManageCommission = ({ const { t } = useTranslation('modals'); const { openHelp } = useHelp(); const { - api, poolsConfig: { globalMaxCommission }, } = useApi(); - const { newBatchCall } = useBatchCall(); - const { activeAccount } = useActiveAccounts(); - const { setModalStatus } = useOverlay().modal; - const { isOwner, activePool } = useActivePool(); - const { getSignerWarnings } = useSignerWarnings(); - const { getBondedPool, updateBondedPools } = useBondedPools(); const { getInitial, getCurrent, @@ -55,6 +49,13 @@ export const ManageCommission = ({ resetAll, isUpdated, } = usePoolCommission(); + const { network } = useNetwork(); + const { newBatchCall } = useBatchCall(); + const { activeAccount } = useActiveAccounts(); + const { setModalStatus } = useOverlay().modal; + const { isOwner, activePool } = useActivePool(); + const { getSignerWarnings } = useSignerWarnings(); + const { getBondedPool, updateBondedPools } = useBondedPools(); const poolId = activePool?.id || 0; const bondedPool = getBondedPool(poolId); @@ -125,39 +126,45 @@ export const ManageCommission = ({ // tx to submit. const getTx = () => { - if (!valid || !api) { + const { pApi } = ApiController.get(network); + if (!valid || !pApi) { return null; } const txs = []; if (commissionUpdated) { txs.push( - api.tx.nominationPools.setCommission( - poolId, - currentCommissionSet + pApi.tx.NominationPools.set_commission({ + pool_id: poolId, + new_commission: currentCommissionSet ? [ - new BigNumber(commission).multipliedBy(10000000).toString(), + new BigNumber(commission).multipliedBy(10000000).toNumber(), payee, ] - : null - ) + : undefined, + }) ); } if (isUpdated('max_commission') && getEnabled('max_commission')) { txs.push( - api.tx.nominationPools.setCommissionMax( - poolId, - new BigNumber(maxCommission).multipliedBy(10000000).toString() - ) + pApi.tx.NominationPools.set_commission_max({ + pool_id: poolId, + max_commission: new BigNumber(maxCommission) + .multipliedBy(10000000) + .toNumber(), + }) ); } if (isUpdated('change_rate') && getEnabled('change_rate')) { txs.push( - api.tx.nominationPools.setCommissionChangeRate(poolId, { - maxIncrease: new BigNumber(changeRate.maxIncrease) - .multipliedBy(10000000) - .toString(), - minDelay: changeRate.minDelay.toString(), + pApi.tx.NominationPools.set_commission_change_rate({ + pool_id: poolId, + change_rate: { + max_increase: new BigNumber(changeRate.maxIncrease) + .multipliedBy(10000000) + .toNumber(), + min_delay: changeRate.minDelay.toNumber(), + }, }) ); } diff --git a/packages/app/src/modals/ManagePool/Forms/RenamePool/index.tsx b/packages/app/src/modals/ManagePool/Forms/RenamePool/index.tsx index fbc76b8a60..bb8b8f0a1a 100644 --- a/packages/app/src/modals/ManagePool/Forms/RenamePool/index.tsx +++ b/packages/app/src/modals/ManagePool/Forms/RenamePool/index.tsx @@ -4,9 +4,10 @@ import { faChevronLeft } from '@fortawesome/free-solid-svg-icons'; import { u8aToString, u8aUnwrapBytes } from '@polkadot/util'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useApi } from 'contexts/Api'; +import { useNetwork } from 'contexts/Network'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { useBondedPools } from 'contexts/Pools/BondedPools'; +import { ApiController } from 'controllers/Api'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { useOverlay } from 'kits/Overlay/Provider'; @@ -14,6 +15,7 @@ import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'; import { Warning } from 'library/Form/Warning'; import { SubmitTx } from 'library/SubmitTx'; +import { Binary } from 'polkadot-api'; import type { Dispatch, FormEvent, SetStateAction } from 'react'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -27,7 +29,7 @@ export const RenamePool = ({ section: number; }) => { const { t } = useTranslation('modals'); - const { api } = useApi(); + const { network } = useNetwork(); const { setModalStatus } = useOverlay().modal; const { activeAccount } = useActiveAccounts(); const { isOwner, activePool } = useActivePool(); @@ -58,10 +60,14 @@ export const RenamePool = ({ // tx to submit const getTx = () => { - if (!valid || !api) { + const { pApi } = ApiController.get(network); + if (!valid || !pApi || !poolId) { return null; } - return api.tx.nominationPools.setMetadata(poolId, metadata); + return pApi.tx.NominationPools.set_metadata({ + pool_id: poolId, + metadata: Binary.fromText(metadata), + }); }; const submitExtrinsic = useSubmitExtrinsic({ diff --git a/packages/app/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx b/packages/app/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx index bb4e857341..0c8ee70b15 100644 --- a/packages/app/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx +++ b/packages/app/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx @@ -3,11 +3,12 @@ import { faChevronLeft } from '@fortawesome/free-solid-svg-icons'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useApi } from 'contexts/Api'; import { useBalances } from 'contexts/Balances'; +import { useNetwork } from 'contexts/Network'; import { useActivePool } from 'contexts/Pools/ActivePool'; import type { ClaimPermission } from 'contexts/Pools/types'; import { defaultClaimPermission } from 'controllers/ActivePools/defaults'; +import { ApiController } from 'controllers/Api'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { useOverlay } from 'kits/Overlay/Provider'; @@ -29,7 +30,7 @@ export const SetClaimPermission = ({ setSection: Dispatch>; }) => { const { t } = useTranslation('modals'); - const { api } = useApi(); + const { network } = useNetwork(); const { getPoolMembership } = useBalances(); const { activeAccount } = useActiveAccounts(); const { setModalStatus } = useOverlay().modal; @@ -60,10 +61,16 @@ export const SetClaimPermission = ({ // tx to submit. const getTx = () => { - if (!valid || !api) { + const { pApi } = ApiController.get(network); + if (!valid || !pApi || !claimPermission) { return null; } - return api.tx.nominationPools.setClaimPermission(claimPermission); + return pApi.tx.NominationPools.set_claim_permission({ + permission: { + type: claimPermission, + value: undefined, + }, + }); }; const submitExtrinsic = useSubmitExtrinsic({ diff --git a/packages/app/src/modals/ManagePool/Forms/SetPoolState/index.tsx b/packages/app/src/modals/ManagePool/Forms/SetPoolState/index.tsx index 7363d6ef12..dbec6c1a54 100644 --- a/packages/app/src/modals/ManagePool/Forms/SetPoolState/index.tsx +++ b/packages/app/src/modals/ManagePool/Forms/SetPoolState/index.tsx @@ -3,9 +3,10 @@ import { faChevronLeft } from '@fortawesome/free-solid-svg-icons'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useApi } from 'contexts/Api'; +import { useNetwork } from 'contexts/Network'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { useBondedPools } from 'contexts/Pools/BondedPools'; +import { ApiController } from 'controllers/Api'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { useOverlay } from 'kits/Overlay/Provider'; @@ -27,7 +28,7 @@ export const SetPoolState = ({ task?: string; }) => { const { t } = useTranslation('modals'); - const { api } = useApi(); + const { network } = useNetwork(); const { setModalStatus } = useOverlay().modal; const { activeAccount } = useActiveAccounts(); const { getSignerWarnings } = useSignerWarnings(); @@ -80,20 +81,30 @@ export const SetPoolState = ({ // tx to submit const getTx = () => { - if (!valid || !api) { + const { pApi } = ApiController.get(network); + if (!valid || !pApi || poolId === undefined) { return null; } let tx; switch (task) { case 'destroy_pool': - tx = api.tx.nominationPools.setState(poolId, 'Destroying'); + tx = pApi.tx.NominationPools.set_state({ + pool_id: poolId, + state: { type: 'Destroying', value: undefined }, + }); break; case 'unlock_pool': - tx = api.tx.nominationPools.setState(poolId, 'Open'); + tx = pApi.tx.NominationPools.set_state({ + pool_id: poolId, + state: { type: 'Open', value: undefined }, + }); break; case 'lock_pool': - tx = api.tx.nominationPools.setState(poolId, 'Blocked'); + tx = pApi.tx.NominationPools.set_state({ + pool_id: poolId, + state: { type: 'Blocked', value: undefined }, + }); break; default: tx = null; diff --git a/packages/app/src/modals/StopNominations/index.tsx b/packages/app/src/modals/StopNominations/index.tsx index fdab2aa6d5..d0025800c6 100644 --- a/packages/app/src/modals/StopNominations/index.tsx +++ b/packages/app/src/modals/StopNominations/index.tsx @@ -2,11 +2,12 @@ // SPDX-License-Identifier: GPL-3.0-only import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useApi } from 'contexts/Api'; import { useBalances } from 'contexts/Balances'; import { useBonded } from 'contexts/Bonded'; +import { useNetwork } from 'contexts/Network'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { useTxMeta } from 'contexts/TxMeta'; +import { ApiController } from 'controllers/Api'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { useOverlay } from 'kits/Overlay/Provider'; @@ -21,7 +22,7 @@ import { useTranslation } from 'react-i18next'; export const StopNominations = () => { const { t } = useTranslation('modals'); - const { api } = useApi(); + const { network } = useNetwork(); const { notEnoughFunds } = useTxMeta(); const { getBondedAccount } = useBonded(); const { getNominations } = useBalances(); @@ -62,16 +63,17 @@ export const StopNominations = () => { // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; - if (!valid || !api) { + if (!valid || !pApi) { return tx; } if (isPool) { // wishing to stop all nominations, call chill - tx = api.tx.nominationPools.chill(activePool?.id || 0); + tx = pApi.tx.NominationPools.chill({ pool_id: activePool?.id || 0 }); } else if (isStaking) { - tx = api.tx.staking.chill(); + tx = pApi.tx.Staking.chill(); } return tx; }; diff --git a/packages/app/src/modals/Unbond/index.tsx b/packages/app/src/modals/Unbond/index.tsx index 31396251f7..bfaa9348bc 100644 --- a/packages/app/src/modals/Unbond/index.tsx +++ b/packages/app/src/modals/Unbond/index.tsx @@ -10,6 +10,7 @@ import { useNetwork } from 'contexts/Network'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { useTransferOptions } from 'contexts/TransferOptions'; import { useTxMeta } from 'contexts/TxMeta'; +import { ApiController } from 'controllers/Api'; import { getUnixTime } from 'date-fns'; import { useErasToTimeLeft } from 'hooks/useErasToTimeLeft'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; @@ -34,6 +35,7 @@ export const Unbond = () => { const { notEnoughFunds } = useTxMeta(); const { getBondedAccount } = useBonded(); const { + network, networkData: { units, unit }, } = useNetwork(); const { erasToSeconds } = useErasToTimeLeft(); @@ -47,7 +49,6 @@ export const Unbond = () => { config: { options }, } = useOverlay().modal; const { - api, consts, poolsConfig: { minJoinBond: minJoinBondBn, minCreateBond: minCreateBondBn }, } = useApi(); @@ -104,8 +105,9 @@ export const Unbond = () => { // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; - if (!api || !activeAccount) { + if (!pApi || !activeAccount) { return tx; } @@ -116,9 +118,12 @@ export const Unbond = () => { // determine tx if (isPooling) { - tx = api.tx.nominationPools.unbond(activeAccount, bondToSubmit); + tx = pApi.tx.NominationPools.unbond({ + member_account: { type: 'Id', value: activeAccount }, + unbonding_points: BigInt(bondToSubmit), + }); } else if (isStaking) { - tx = api.tx.staking.unbond(bondToSubmit); + tx = pApi.tx.Staking.unbond({ value: BigInt(bondToSubmit) }); } return tx; }; diff --git a/packages/app/src/modals/UnlockChunks/Forms.tsx b/packages/app/src/modals/UnlockChunks/Forms.tsx index 2b015e6726..98a5b9d889 100644 --- a/packages/app/src/modals/UnlockChunks/Forms.tsx +++ b/packages/app/src/modals/UnlockChunks/Forms.tsx @@ -13,6 +13,7 @@ import { useActivePool } from 'contexts/Pools/ActivePool'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { useFavoritePools } from 'contexts/Pools/FavoritePools'; import { usePoolMembers } from 'contexts/Pools/PoolMembers'; +import { ApiController } from 'controllers/Api'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { useOverlay } from 'kits/Overlay/Provider'; @@ -22,12 +23,11 @@ import { ActionItem } from 'library/ActionItem'; import { Warning } from 'library/Form/Warning'; import { SubmitTx } from 'library/SubmitTx'; import { planckToUnitBn } from 'library/Utils'; -import type { ForwardedRef } from 'react'; -import { forwardRef, useEffect, useState } from 'react'; +import { forwardRef, useEffect, useState, type ForwardedRef } from 'react'; import { useTranslation } from 'react-i18next'; import { ButtonSubmitInvert } from 'ui-buttons'; -import { ContentWrapper } from './Wrappers'; import type { FormsProps } from './types'; +import { ContentWrapper } from './Wrappers'; export const Forms = forwardRef( ( @@ -35,8 +35,9 @@ export const Forms = forwardRef( ref: ForwardedRef ) => { const { t } = useTranslation('modals'); - const { api, consts } = useApi(); + const { consts } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); const { activePool } = useActivePool(); @@ -67,20 +68,30 @@ export const Forms = forwardRef( // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; - if (!valid || !api || !unlock) { + if (!valid || !pApi || !unlock) { return tx; } // rebond is only available when staking directly. if (task === 'rebond' && isStaking) { - tx = api.tx.staking.rebond(unlock.value.toNumber() || 0); + tx = pApi.tx.Staking.rebond({ + value: BigInt(unlock.value.toNumber() || 0), + }); } else if (task === 'withdraw' && isStaking) { - tx = api.tx.staking.withdrawUnbonded(historyDepth.toString()); + tx = pApi.tx.Staking.withdraw_unbonded({ + num_slashing_spans: historyDepth.toNumber(), + }); } else if (task === 'withdraw' && isPooling && activePool) { - tx = api.tx.nominationPools.withdrawUnbonded( - activeAccount, - historyDepth.toString() - ); + if (activeAccount) { + tx = pApi.tx.NominationPools.withdraw_unbonded({ + member_account: { + type: 'Id', + value: activeAccount, + }, + num_slashing_spans: historyDepth.toNumber(), + }); + } } return tx; }; diff --git a/packages/app/src/modals/Unstake/index.tsx b/packages/app/src/modals/Unstake/index.tsx index 1a7d82b25f..5a9602ec92 100644 --- a/packages/app/src/modals/Unstake/index.tsx +++ b/packages/app/src/modals/Unstake/index.tsx @@ -9,6 +9,7 @@ import { useBonded } from 'contexts/Bonded'; import { useNetwork } from 'contexts/Network'; import { useTransferOptions } from 'contexts/TransferOptions'; import { useTxMeta } from 'contexts/TxMeta'; +import { ApiController } from 'controllers/Api'; import { getUnixTime } from 'date-fns'; import { useBatchCall } from 'hooks/useBatchCall'; import { useErasToTimeLeft } from 'hooks/useErasToTimeLeft'; @@ -29,9 +30,10 @@ import { useTranslation } from 'react-i18next'; export const Unstake = () => { const { t } = useTranslation('modals'); const { + network, networkData: { units, unit }, } = useNetwork(); - const { api, consts } = useApi(); + const { consts } = useApi(); const { notEnoughFunds } = useTxMeta(); const { newBatchCall } = useBatchCall(); const { getBondedAccount } = useBonded(); @@ -80,8 +82,9 @@ export const Unstake = () => { // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); const tx = null; - if (!api || !activeAccount) { + if (!pApi || !activeAccount) { return tx; } // remove decimal errors @@ -91,11 +94,11 @@ export const Unstake = () => { ); if (bondToSubmit == 0n) { - return api.tx.staking.chill(); + return pApi.tx.Staking.chill(); } const txs = [ - api.tx.staking.chill(), - api.tx.staking.unbond(bondToSubmit.toString()), + pApi.tx.Staking.chill(), + pApi.tx.Staking.unbond({ value: BigInt(bondToSubmit.toString()) }), ]; return newBatchCall(txs, controller); }; diff --git a/packages/app/src/modals/UpdatePayee/index.tsx b/packages/app/src/modals/UpdatePayee/index.tsx index 81d6f5e7a0..d64f1730ee 100644 --- a/packages/app/src/modals/UpdatePayee/index.tsx +++ b/packages/app/src/modals/UpdatePayee/index.tsx @@ -3,11 +3,12 @@ import { isValidAddress } from '@w3ux/utils'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useApi } from 'contexts/Api'; import { useBalances } from 'contexts/Balances'; import { useBonded } from 'contexts/Bonded'; +import { useNetwork } from 'contexts/Network'; import type { PayeeConfig, PayeeOptions } from 'contexts/Setup/types'; import { useTxMeta } from 'contexts/TxMeta'; +import { ApiController } from 'controllers/Api'; import { usePayeeConfig } from 'hooks/usePayeeConfig'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; @@ -26,7 +27,7 @@ import type { MaybeAddress } from 'types'; export const UpdatePayee = () => { const { t } = useTranslation('modals'); - const { api } = useApi(); + const { network } = useNetwork(); const { getPayee } = useBalances(); const { notEnoughFunds } = useTxMeta(); const { getBondedAccount } = useBonded(); @@ -72,20 +73,26 @@ export const UpdatePayee = () => { // Tx to submit. const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; + if (!pApi || !selected.destination) { + return tx; + } - if (!api) { + if (selected.destination === 'Account' && !selected.account) { return tx; } - const payeeToSubmit = !isComplete() - ? 'Staked' - : selected.destination === 'Account' - ? { - Account: selected.account, - } - : selected.destination; - tx = api.tx.staking.setPayee(payeeToSubmit); + tx = pApi.tx.Staking.set_payee({ + payee: !isComplete() + ? { type: 'Staked', value: undefined } + : selected.destination === 'Account' + ? { + type: 'Account', + value: selected.account as string, + } + : { type: selected.destination, value: undefined }, + }); return tx; }; diff --git a/packages/app/src/model/Api/index.ts b/packages/app/src/model/Api/index.ts index 67fa86fcbf..d4a2723901 100644 --- a/packages/app/src/model/Api/index.ts +++ b/packages/app/src/model/Api/index.ts @@ -1,36 +1,41 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import { ApiPromise, WsProvider } from '@polkadot/api'; -import { ScProvider } from '@polkadot/rpc-provider/substrate-connect'; -import * as Sc from '@substrate/connect'; -import { WellKnownChain } from '@substrate/connect'; import { NetworkList, SystemChainList } from 'config/networks'; +import { getLightClientMetadata } from 'config/util'; import { SubscriptionsController } from 'controllers/Subscriptions'; -import type { AnyApi, NetworkName, SystemChainId } from 'types'; +import type { PolkadotClient } from 'polkadot-api'; +import { createClient } from 'polkadot-api'; +import { getSmProvider } from 'polkadot-api/sm-provider'; +import { startFromWorker } from 'polkadot-api/smoldot/from-worker'; +import SmWorker from 'polkadot-api/smoldot/worker?worker'; +import { getWsProvider } from 'polkadot-api/ws-provider/web'; +import type { NetworkName, SystemChainId } from 'types'; import type { ApiChainType, APIEventDetail, ConnectionType, EventApiStatus, + PapiApi, + PapiChainSpec, + PapiReadyEvent, } from './types'; export class Api { - // ------------------------------------------------------ - // Class members. - // ------------------------------------------------------ - // The network name associated with this Api instance. network: NetworkName | SystemChainId; // The type of chain being connected to. #chainType: ApiChainType; - // API provider. - #provider: WsProvider | ScProvider; + // PAPI Instance. + #papiClient: PolkadotClient; + + // PAPI API. + #pApi: PapiApi; - // API instance. - #api: ApiPromise; + // PAPI Chain Spec. + #papiChainSpec: PapiChainSpec; // The current RPC endpoint. #rpcEndpoint: string; @@ -38,31 +43,27 @@ export class Api { // The current connection type. #connectionType: ConnectionType; - // ------------------------------------------------------ - // Getters. - // ------------------------------------------------------ + get papiClient() { + return this.#papiClient; + } - get api() { - return this.#api; + get pApi() { + return this.#pApi; + } + + get papiChainSpec() { + return this.#papiChainSpec; } get connectionType() { return this.#connectionType; } - // ------------------------------------------------------ - // Constructor. - // ------------------------------------------------------ - constructor(network: NetworkName | SystemChainId, chainType: ApiChainType) { this.network = network; this.#chainType = chainType; } - // ------------------------------------------------------ - // Initialization. - // ------------------------------------------------------ - // Class initialization. Sets the `provider` and `api` class members. async initialize(type: ConnectionType, rpcEndpoint: string) { // Set connection metadata. @@ -86,14 +87,11 @@ export class Api { // Tell UI api is connecting. this.dispatchEvent(this.ensureEventStatus('connecting')); - // Initialise api. - this.#api = new ApiPromise({ provider: this.#provider }); + // Initialise PAPI API. + this.#pApi = this.#papiClient.getUnsafeApi(); - // Initialise api events. - this.initApiEvents(); - - // Wait for api to be ready. - await this.#api.isReady; + // Fetch chain spec and metadata from PAPI client. + await this.fetchChainSpec(); } catch (e) { // TODO: report a custom api status error that can flag to the UI the rpcEndpoint failed - // retry or select another one. Useful for custom endpoint configs. @@ -101,10 +99,6 @@ export class Api { } } - // ------------------------------------------------------ - // Provider initialization. - // ------------------------------------------------------ - // Initiate Websocket Provider. initWsProvider() { const endpoint = @@ -114,49 +108,101 @@ export class Api { this.#rpcEndpoint ]; - this.#provider = new WsProvider(endpoint); + // Initialize Polkadot API Client. + this.#papiClient = createClient(getWsProvider(endpoint)); } // Dynamically load and connect to Substrate Connect. async initScProvider() { - // Get light client key from network list. - const lightClientKey = - this.#chainType === 'relay' - ? NetworkList[this.network].endpoints.lightClient - : SystemChainList[this.network].endpoints.lightClient; - - // Instantiate light client provider. - this.#provider = new ScProvider( - Sc as AnyApi, - WellKnownChain[lightClientKey as keyof typeof WellKnownChain] + // Initialise PAPI light client. + const smoldot = startFromWorker(new SmWorker()); + const smMetadata = getLightClientMetadata(this.#chainType, this.network); + + const chain = getSmProvider( + smoldot.addChain({ + chainSpec: await smMetadata.chain.fn(), + potentialRelayChains: smMetadata?.relay + ? [await smoldot.addChain({ chainSpec: await smMetadata.relay.fn() })] + : undefined, + }) ); - await this.#provider.connect(); + this.#papiClient = createClient(chain); } - // ------------------------------------------------------ - // Event handling. - // ------------------------------------------------------ - - // Set up API event listeners. Sends information to the UI. - async initApiEvents() { - this.#api.on('ready', async () => { - this.dispatchEvent(this.ensureEventStatus('ready')); - }); + async fetchChainSpec() { + try { + const chainSpecData = await this.#papiClient.getChainSpecData(); + const version = await this.#pApi.constants.System.Version(); + + const { genesisHash, properties } = chainSpecData; + const { ss58Format, tokenDecimals, tokenSymbol } = properties; + const { + authoring_version: authoringVersion, + impl_name: implName, + impl_version: implVersion, + spec_name: specName, + spec_version: specVersion, + state_version: stateVersion, + transaction_version: transactionVersion, + } = version; + + this.#papiChainSpec = { + genesisHash, + ss58Format, + tokenDecimals, + tokenSymbol, + authoringVersion, + implName, + implVersion, + specName, + specVersion, + stateVersion, + transactionVersion, + }; + + // Dispatch ready eventd to let contexts populate constants. + this.dispatchReadyEvent(); + } catch (e) { + // TODO: Expand this when PJS API has been removed. Flag an error if there are any issues + // bootstrapping chain spec. NOTE: This can happen when PAPI is the standalone connection + // method. + //this.dispatchEvent(this.ensureEventStatus('error'), { err: 'ChainSpecError' }); + } + } - this.#api.on('connected', () => { - this.dispatchEvent(this.ensureEventStatus('connected')); - }); + // Get a pallet constant, with a fallback value. + getConstant = async ( + pallet: string, + key: string, + fallback: T, + formatter?: 'asBytes' + ): Promise => { + try { + const result = await this.#pApi.constants[pallet][key](); - this.#api.on('disconnected', () => { - this.dispatchEvent(this.ensureEventStatus('disconnected')); - }); + switch (formatter) { + case 'asBytes': + return result.asBytes(); + default: + return result; + } + } catch (e) { + return fallback; + } + }; - this.#api.on('error', () => { - this.dispatchEvent(this.ensureEventStatus('error')); - }); + // Handler for dispatching ready events. + dispatchReadyEvent() { + const detail: PapiReadyEvent = { + network: this.network, + chainType: this.#chainType, + ...this.#papiChainSpec, + }; + this.dispatchEvent(this.ensureEventStatus('ready')); + document.dispatchEvent(new CustomEvent('papi-ready', { detail })); } - // Handler for dispatching events. + // Handler for dispatching `api-status` events. dispatchEvent( status: EventApiStatus, options?: { @@ -176,10 +222,6 @@ export class Api { document.dispatchEvent(new CustomEvent('api-status', { detail })); } - // ------------------------------------------------------ - // Class helpers. - // ------------------------------------------------------ - // Ensures the provided status is a valid `EventStatus` being passed, or falls back to `error`. ensureEventStatus = (status: string | EventApiStatus): EventApiStatus => { const eventStatus: string[] = [ @@ -207,17 +249,12 @@ export class Api { } }; - // ------------------------------------------------------ - // Disconnect. - // ------------------------------------------------------ - // Disconnect gracefully from API and provider. async disconnect(destroy = false) { this.unsubscribe(); - // Disconnect provider and api. - await this.#provider?.disconnect(); - await this.#api?.disconnect(); + // Disconnect from PAPI Client. + this.#papiClient?.destroy(); // Tell UI Api is destroyed. if (destroy) { diff --git a/packages/app/src/model/Api/types.ts b/packages/app/src/model/Api/types.ts index 5129cb4639..efbea3b025 100644 --- a/packages/app/src/model/Api/types.ts +++ b/packages/app/src/model/Api/types.ts @@ -1,6 +1,7 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only +import type { UnsafeApi } from 'polkadot-api'; import type { NetworkName, SystemChainId } from 'types'; export interface APIConfig { @@ -17,6 +18,27 @@ export interface APIEventDetail { err?: string; } +export interface PapiChainSpec { + genesisHash: string; + ss58Format: number; + tokenDecimals: number; + tokenSymbol: string; + authoringVersion: number; + implName: string; + implVersion: number; + specName: string; + specVersion: number; + stateVersion: number; + transactionVersion: number; +} + +export type PapiReadyEvent = PapiChainSpec & { + network: NetworkName | SystemChainId; + chainType: string; +}; + +export type PapiApi = UnsafeApi; + export type ConnectionType = 'ws' | 'sc'; export type ApiStatus = 'connecting' | 'connected' | 'disconnected' | 'ready'; diff --git a/packages/app/src/model/Entries/BondedPools/index.tsx b/packages/app/src/model/Entries/BondedPools/index.tsx new file mode 100644 index 0000000000..4f34b56005 --- /dev/null +++ b/packages/app/src/model/Entries/BondedPools/index.tsx @@ -0,0 +1,84 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import BigNumber from 'bignumber.js'; +import { perbillToPercent } from 'library/Utils'; +import type { PapiApi } from 'model/Api/types'; +import type { AnyApi } from 'types'; + +export class BondedPools { + #pApi: PapiApi; + + bondedPools: AnyApi = {}; + + constructor(pApi: PapiApi) { + this.#pApi = pApi; + } + + async fetch() { + this.bondedPools = + await this.#pApi.query.NominationPools.BondedPools.getEntries({ + at: 'best', + }); + return this; + } + + async fetchOne(id: number) { + const result = await this.#pApi.query.NominationPools.BondedPools.getValue( + id, + { at: 'best' } + ); + + if (!result) { + return null; + } + return this.formatPool(result); + } + + format(entry?: AnyApi) { + return Object.fromEntries( + (entry ? [entry] : this.bondedPools).map( + ({ keyArgs, value }: { keyArgs: [number]; value: AnyApi }) => { + const id = keyArgs[0]; + const pool = this.formatPool(value); + return [id, pool]; + } + ) + ); + } + + formatPool(value: AnyApi) { + const maybeCommissionCurrent = value.commission.current; + const commissionCurrent = !maybeCommissionCurrent + ? null + : [ + perbillToPercent(maybeCommissionCurrent[0]).toString(), + maybeCommissionCurrent[1], + ]; + + const commissionMax = value.commission.max; + const commissionMaxPercent = !commissionMax + ? null + : perbillToPercent(new BigNumber(value.commission.max)); + + const commissionChangeRate = value.commission.change_rate; + + const commission = { + current: commissionCurrent, + claimPermission: value.commission.claim_permission?.type || null, + max: commissionMaxPercent, + changeRate: commissionChangeRate || null, + throttleFrom: value.commission.throttle_from || null, + }; + + const pool = { + commission, + points: value.points.toString(), + memberCounter: value.member_counter.toString(), + roles: value.roles, + state: value.state.type, + }; + + return pool; + } +} diff --git a/packages/app/src/model/Entries/Validators/index.ts b/packages/app/src/model/Entries/Validators/index.ts new file mode 100644 index 0000000000..b5eea430d5 --- /dev/null +++ b/packages/app/src/model/Entries/Validators/index.ts @@ -0,0 +1,16 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class Validators { + #pApi: PapiApi; + + constructor(pApi: PapiApi) { + this.#pApi = pApi; + } + + async fetch() { + return await this.#pApi.query.Staking.Validators.getEntries({ at: 'best' }); + } +} diff --git a/packages/app/src/model/ErasStakersPaged/index.tsx b/packages/app/src/model/ErasStakersPaged/index.tsx new file mode 100644 index 0000000000..9eefda7db7 --- /dev/null +++ b/packages/app/src/model/ErasStakersPaged/index.tsx @@ -0,0 +1,20 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ErasStakersPaged { + #pApi: PapiApi; + + constructor(pApi: PapiApi) { + this.#pApi = pApi; + } + + async fetch(era: number, validator: string) { + return await this.#pApi.query.Staking.ErasStakersPaged.getEntries( + era, + validator, + { at: 'best' } + ); + } +} diff --git a/packages/app/src/model/Query/BondedMulti/index.ts b/packages/app/src/model/Query/BondedMulti/index.ts new file mode 100644 index 0000000000..80fb032ad6 --- /dev/null +++ b/packages/app/src/model/Query/BondedMulti/index.ts @@ -0,0 +1,31 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class BondedMulti { + #pApi: PapiApi; + + #addresses: [string][]; + + constructor(pApi: PapiApi, eras: [string][]) { + this.#pApi = pApi; + this.#addresses = eras; + } + + async fetch() { + try { + const results = await this.#pApi.query.Staking.Bonded.getValues( + this.#addresses, + { + at: 'best', + } + ); + return results; + } catch (e) { + // Silently fail. + } + + return []; + } +} diff --git a/packages/app/src/model/Query/ClaimedRewards/index.ts b/packages/app/src/model/Query/ClaimedRewards/index.ts new file mode 100644 index 0000000000..1d3d39e5ec --- /dev/null +++ b/packages/app/src/model/Query/ClaimedRewards/index.ts @@ -0,0 +1,33 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ClaimedRewards { + #pApi: PapiApi; + #era: number; + #address: string; + + constructor(pApi: PapiApi, era: number, address: string) { + this.#pApi = pApi; + this.#era = era; + this.#address = address; + } + + async fetch() { + try { + const result = await this.#pApi.query.Staking.ClaimedRewards.getValue( + this.#era, + this.#address, + { + at: 'best', + } + ); + return result; + } catch (e) { + // Silently fail. + } + + return undefined; + } +} diff --git a/packages/app/src/model/Query/Era/index.ts b/packages/app/src/model/Query/Era/index.ts index 875984527a..2c5c916992 100644 --- a/packages/app/src/model/Query/Era/index.ts +++ b/packages/app/src/model/Query/Era/index.ts @@ -1,24 +1,41 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { ApiPromise } from '@polkadot/api'; import BigNumber from 'bignumber.js'; -import type { AnyApi } from 'types'; +import type { PapiApi } from 'model/Api/types'; export class Era { - // Fetch network constants. - async fetch(api: ApiPromise) { - // Fetch the active era. - const result = JSON.parse( - ((await api.query.staking.activeEra()) as AnyApi) - .unwrapOrDefault() - .toString() - ); + #pApi: PapiApi; + + constructor(pApi: PapiApi) { + this.#pApi = pApi; + } + + async fetch() { + let era = { + start: 0n, + index: 0, + }; + + try { + const result = await this.#pApi.query.Staking.ActiveEra.getValue({ + at: 'best', + }); + + if (result) { + era = { + start: result?.start || 0n, + index: result.index, + }; + } + } catch (e) { + // Silent fail. + } - // Store active era. + // Store active era as BigNumbers. const activeEra = { - index: new BigNumber(result.index), - start: new BigNumber(result.start), + start: new BigNumber(era.start.toString()), + index: new BigNumber(era.index), }; // Get previous era. diff --git a/packages/app/src/model/Query/ErasRewardPoints/index.ts b/packages/app/src/model/Query/ErasRewardPoints/index.ts new file mode 100644 index 0000000000..47452cc24a --- /dev/null +++ b/packages/app/src/model/Query/ErasRewardPoints/index.ts @@ -0,0 +1,31 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ErasRewardPoints { + #pApi: PapiApi; + + #era: number; + + constructor(pApi: PapiApi, era: number) { + this.#pApi = pApi; + this.#era = era; + } + + async fetch() { + try { + const result = await this.#pApi.query.Staking.ErasRewardPoints.getValue( + this.#era, + { + at: 'best', + } + ); + return result; + } catch (e) { + // Silently fail. + } + + return []; + } +} diff --git a/packages/app/src/model/Query/ErasRewardPointsMulti/index.ts b/packages/app/src/model/Query/ErasRewardPointsMulti/index.ts new file mode 100644 index 0000000000..5c094fce1a --- /dev/null +++ b/packages/app/src/model/Query/ErasRewardPointsMulti/index.ts @@ -0,0 +1,31 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ErasRewardPointsMulti { + #pApi: PapiApi; + + #eras: [number][]; + + constructor(pApi: PapiApi, eras: [number][]) { + this.#pApi = pApi; + this.#eras = eras; + } + + async fetch() { + try { + const results = await this.#pApi.query.Staking.ErasRewardPoints.getValues( + this.#eras, + { + at: 'best', + } + ); + return results; + } catch (e) { + // Silently fail. + } + + return []; + } +} diff --git a/packages/app/src/model/Query/ErasStakersOverview/index.tsx b/packages/app/src/model/Query/ErasStakersOverview/index.tsx new file mode 100644 index 0000000000..37ad396f2a --- /dev/null +++ b/packages/app/src/model/Query/ErasStakersOverview/index.tsx @@ -0,0 +1,18 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ErasStakersOverview { + #pApi: PapiApi; + + constructor(pApi: PapiApi) { + this.#pApi = pApi; + } + + async fetch(era: number) { + return await this.#pApi.query.Staking.ErasStakersOverview.getEntries(era, { + at: 'best', + }); + } +} diff --git a/packages/app/src/model/Query/ErasValidatorReward/index.ts b/packages/app/src/model/Query/ErasValidatorReward/index.ts new file mode 100644 index 0000000000..3ba3c1743f --- /dev/null +++ b/packages/app/src/model/Query/ErasValidatorReward/index.ts @@ -0,0 +1,28 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ErasValidatorReward { + #pApi: PapiApi; + #era: number; + + constructor(pApi: PapiApi, era: number) { + this.#pApi = pApi; + this.#era = era; + } + + async fetch() { + try { + const result = + await this.#pApi.query.Staking.ErasValidatorReward.getValue(this.#era, { + at: 'best', + }); + return result; + } catch (e) { + // Silently fail. + } + + return []; + } +} diff --git a/packages/app/src/model/Query/ErasValidatorRewardMulti/index.ts b/packages/app/src/model/Query/ErasValidatorRewardMulti/index.ts new file mode 100644 index 0000000000..ada87a6fcb --- /dev/null +++ b/packages/app/src/model/Query/ErasValidatorRewardMulti/index.ts @@ -0,0 +1,31 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ErasValidatorReward { + #pApi: PapiApi; + #eras: [number][]; + + constructor(pApi: PapiApi, eras: [number][]) { + this.#pApi = pApi; + this.#eras = eras; + } + + async fetch() { + try { + const results = + await this.#pApi.query.Staking.ErasValidatorReward.getValues( + this.#eras, + { + at: 'best', + } + ); + return results; + } catch (e) { + // Silently fail. + } + + return []; + } +} diff --git a/packages/app/src/model/Query/IdentityOfMulti/index.ts b/packages/app/src/model/Query/IdentityOfMulti/index.ts new file mode 100644 index 0000000000..f96df081d5 --- /dev/null +++ b/packages/app/src/model/Query/IdentityOfMulti/index.ts @@ -0,0 +1,31 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class IdentityOfMulti { + #pApi: PapiApi; + + #addresses: [string][]; + + constructor(pApi: PapiApi, addresses: [string][]) { + this.#pApi = pApi; + this.#addresses = addresses; + } + + async fetch() { + try { + const result = await this.#pApi.query.Identity.IdentityOf.getValues( + this.#addresses, + { + at: 'best', + } + ); + return result; + } catch (e) { + // Silently fail. + } + + return null; + } +} diff --git a/packages/app/src/model/Query/NetworkMeta/index.ts b/packages/app/src/model/Query/NetworkMeta/index.ts index ae8839fff4..61fd9d9785 100644 --- a/packages/app/src/model/Query/NetworkMeta/index.ts +++ b/packages/app/src/model/Query/NetworkMeta/index.ts @@ -1,97 +1,128 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { ApiPromise } from '@polkadot/api'; -import { rmCommas } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import type { APIActiveEra } from 'contexts/Api/types'; -import { stringToBn } from 'library/Utils'; +import { perbillToPercent, stringToBn } from 'library/Utils'; +import type { PapiApi } from 'model/Api/types'; export class NetworkMeta { + #pApi: PapiApi; + + constructor(pApi: PapiApi) { + this.#pApi = pApi; + } + // Fetch network constants. - async fetch( - api: ApiPromise, - activeEra: APIActiveEra, - previousEra: BigNumber - ) { - // Fetch network configuration. - const networkMeta = await api.queryMulti([ - // Network metrics. - api.query.balances.totalIssuance, - api.query.auctions.auctionCounter, - api.query.paraSessionInfo.earliestStoredSession, - api.query.fastUnstake.erasToCheckPerBlock, - api.query.staking.minimumActiveStake, - // Nomination pool configs. - api.query.nominationPools.counterForPoolMembers, - api.query.nominationPools.counterForBondedPools, - api.query.nominationPools.counterForRewardPools, - api.query.nominationPools.lastPoolId, - api.query.nominationPools.maxPoolMembers, - api.query.nominationPools.maxPoolMembersPerPool, - api.query.nominationPools.maxPools, - api.query.nominationPools.minCreateBond, - api.query.nominationPools.minJoinBond, - api.query.nominationPools.globalMaxCommission, - // Staking metrics. - api.query.staking.counterForNominators, - api.query.staking.counterForValidators, - api.query.staking.maxValidatorsCount, - api.query.staking.validatorCount, - [api.query.staking.erasValidatorReward, previousEra.toString()], - [api.query.staking.erasTotalStake, previousEra.toString()], - api.query.staking.minNominatorBond, - [api.query.staking.erasTotalStake, activeEra.index.toString()], - api.query.staking.counterForNominators, + async fetch(activeEra: APIActiveEra, previousEra: BigNumber) { + const at = { at: 'best' }; + const totalIssuance = + await this.#pApi.query.Balances.TotalIssuance.getValue(at); + + const [ + auctionCounter, + earliestStoredSession, + erasToCheckPerBlock, + minimumActiveStake, + counterForPoolMembers, + counterForBondedPools, + counterForRewardPools, + lastPoolId, + maxPoolMembersRaw, + maxPoolMembersPerPoolRaw, + maxPoolsRaw, + minCreateBond, + minJoinBond, + globalMaxCommission, + counterForNominators, + counterForValidators, + maxValidatorsCount, + validatorCount, + prevErasValidatorReward, + prevEraErasTotalStake, + minNominatorBond, + activeEraErasTotalStake, + ] = await Promise.all([ + this.#pApi.query.Auctions.AuctionCounter.getValue(at), + this.#pApi.query.ParaSessionInfo.EarliestStoredSession.getValue(at), + this.#pApi.query.FastUnstake.ErasToCheckPerBlock.getValue(at), + this.#pApi.query.Staking.MinimumActiveStake.getValue(at), + this.#pApi.query.NominationPools.CounterForPoolMembers.getValue(at), + this.#pApi.query.NominationPools.CounterForBondedPools.getValue(at), + this.#pApi.query.NominationPools.CounterForRewardPools.getValue(at), + this.#pApi.query.NominationPools.LastPoolId.getValue(at), + this.#pApi.query.NominationPools.MaxPoolMembers.getValue(at), + this.#pApi.query.NominationPools.MaxPoolMembersPerPool.getValue(at), + this.#pApi.query.NominationPools.MaxPools.getValue(at), + this.#pApi.query.NominationPools.MinCreateBond.getValue(at), + this.#pApi.query.NominationPools.MinJoinBond.getValue(at), + this.#pApi.query.NominationPools.GlobalMaxCommission.getValue(at), + this.#pApi.query.Staking.CounterForNominators.getValue(at), + this.#pApi.query.Staking.CounterForValidators.getValue(at), + this.#pApi.query.Staking.MaxValidatorsCount.getValue(at), + this.#pApi.query.Staking.ValidatorCount.getValue(at), + this.#pApi.query.Staking.ErasValidatorReward.getValue( + previousEra.toNumber(), + at + ), + this.#pApi.query.Staking.ErasTotalStake.getValue( + previousEra.toNumber(), + at + ), + this.#pApi.query.Staking.MinNominatorBond.getValue(at), + this.#pApi.query.Staking.ErasTotalStake.getValue( + activeEra.index.toNumber(), + at + ), ]); - // format optional configs to BigNumber or null. - const maxPoolMembers = networkMeta[9].toHuman() - ? new BigNumber(rmCommas(networkMeta[9].toString())) - : null; + // Format globalMaxCommission from a perbill to a percent. + const globalMaxCommissionAsPercent = !globalMaxCommission + ? new BigNumber(0) + : perbillToPercent(globalMaxCommission); - const maxPoolMembersPerPool = networkMeta[10].toHuman() - ? new BigNumber(rmCommas(networkMeta[10].toString())) + // Format max pool members to be a BigNumber, or null if it's not set. + const maxPoolMembers = maxPoolMembersRaw + ? new BigNumber(maxPoolMembersRaw.toString()) : null; - const maxPools = networkMeta[11].toHuman() - ? new BigNumber(rmCommas(networkMeta[11].toString())) + // Format max pool members per pool to be a BigNumber, or null if it's not set. + const maxPoolMembersPerPool = maxPoolMembersPerPoolRaw + ? new BigNumber(maxPoolMembersPerPoolRaw.toString()) : null; + // Format max pools to be a BigNumber, or null if it's not set. + const maxPools = maxPoolsRaw ? new BigNumber(maxPoolsRaw.toString()) : null; + return { networkMetrics: { - totalIssuance: new BigNumber(networkMeta[0].toString()), - auctionCounter: new BigNumber(networkMeta[1].toString()), - earliestStoredSession: new BigNumber(networkMeta[2].toString()), - fastUnstakeErasToCheckPerBlock: Number( - rmCommas(networkMeta[3].toString()) - ), - minimumActiveStake: new BigNumber(networkMeta[4].toString()), + totalIssuance: new BigNumber(totalIssuance.toString()), + auctionCounter: new BigNumber(auctionCounter.toString()), + earliestStoredSession: new BigNumber(earliestStoredSession.toString()), + fastUnstakeErasToCheckPerBlock: Number(erasToCheckPerBlock.toString()), + minimumActiveStake: new BigNumber(minimumActiveStake.toString()), }, poolsConfig: { - counterForPoolMembers: stringToBn(networkMeta[5].toString()), - counterForBondedPools: stringToBn(networkMeta[6].toString()), - counterForRewardPools: stringToBn(networkMeta[7].toString()), - lastPoolId: stringToBn(networkMeta[8].toString()), + counterForPoolMembers: stringToBn(counterForPoolMembers.toString()), + counterForBondedPools: stringToBn(counterForBondedPools.toString()), + counterForRewardPools: stringToBn(counterForRewardPools.toString()), + lastPoolId: stringToBn(lastPoolId.toString()), maxPoolMembers, maxPoolMembersPerPool, maxPools, - minCreateBond: stringToBn(networkMeta[12].toString()), - minJoinBond: stringToBn(networkMeta[13].toString()), - globalMaxCommission: Number( - String(networkMeta[14]?.toHuman() || '100%').slice(0, -1) - ), + minCreateBond: stringToBn(minCreateBond.toString()), + minJoinBond: stringToBn(minJoinBond.toString()), + globalMaxCommission: Number(globalMaxCommissionAsPercent.toString()), }, stakingMetrics: { - totalNominators: stringToBn(networkMeta[15].toString()), - totalValidators: stringToBn(networkMeta[16].toString()), - maxValidatorsCount: stringToBn(networkMeta[17].toString()), - validatorCount: stringToBn(networkMeta[18].toString()), - lastReward: stringToBn(networkMeta[19].toString()), - lastTotalStake: stringToBn(networkMeta[20].toString()), - minNominatorBond: stringToBn(networkMeta[21].toString()), - totalStaked: stringToBn(networkMeta[22].toString()), - counterForNominators: stringToBn(networkMeta[23].toString()), + totalValidators: stringToBn(counterForValidators.toString()), + maxValidatorsCount: stringToBn(maxValidatorsCount?.toString() || '0'), + validatorCount: stringToBn(validatorCount.toString()), + lastReward: stringToBn(prevErasValidatorReward?.toString() || '0'), + lastTotalStake: stringToBn(prevEraErasTotalStake.toString()), + minNominatorBond: stringToBn(minNominatorBond.toString()), + totalStaked: stringToBn(activeEraErasTotalStake.toString()), + counterForNominators: stringToBn(counterForNominators.toString()), }, }; } diff --git a/packages/app/src/model/Query/NominatorsMulti/index.ts b/packages/app/src/model/Query/NominatorsMulti/index.ts new file mode 100644 index 0000000000..7f93d4e79d --- /dev/null +++ b/packages/app/src/model/Query/NominatorsMulti/index.ts @@ -0,0 +1,40 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class NominatorsMulti { + #pApi: PapiApi; + + #addresses: [string][]; + + constructor(pApi: PapiApi, addresses: [string][]) { + this.#pApi = pApi; + this.#addresses = addresses; + } + + async fetch() { + let result; + try { + result = await this.#pApi.query.Staking.Nominators.getValues( + this.#addresses, + { at: 'best' } + ); + + return result.map((nominator) => { + if (!nominator) { + return undefined; + } + return { + submittedIn: String(nominator.submitted_in), + suppressed: nominator.suppressed, + targets: nominator.targets, + }; + }); + } catch (e) { + // Silently fail. + } + + return null; + } +} diff --git a/packages/app/src/model/Query/ParaSessionAccounts/index.ts b/packages/app/src/model/Query/ParaSessionAccounts/index.ts new file mode 100644 index 0000000000..26d52111fe --- /dev/null +++ b/packages/app/src/model/Query/ParaSessionAccounts/index.ts @@ -0,0 +1,32 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ParaSessionAccounts { + #pApi: PapiApi; + #session: number; + + constructor(pApi: PapiApi, session: number) { + this.#pApi = pApi; + this.#session = session; + } + + async fetch() { + try { + const result = + await this.#pApi.query.ParaSessionInfo.AccountKeys.getValue( + this.#session, + { at: 'best' } + ); + + if (result) { + return result; + } + } catch (e) { + // Silent fail + } + + return []; + } +} diff --git a/packages/app/src/model/Query/PoolMetadataMulti/index.ts b/packages/app/src/model/Query/PoolMetadataMulti/index.ts new file mode 100644 index 0000000000..59f9c836f2 --- /dev/null +++ b/packages/app/src/model/Query/PoolMetadataMulti/index.ts @@ -0,0 +1,29 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class PoolMetadataMulti { + #pApi: PapiApi; + + #ids: [number][]; + + constructor(pApi: PapiApi, ids: [number][]) { + this.#pApi = pApi; + this.#ids = ids; + } + + async fetch() { + try { + const result = await this.#pApi.query.NominationPools.Metadata.getValues( + this.#ids, + { at: 'best' } + ); + return result.map((metadata) => metadata.asText()); + } catch (e) { + // Silently fail. + } + + return []; + } +} diff --git a/packages/app/src/model/Query/ProxiesQuery/index.ts b/packages/app/src/model/Query/ProxiesQuery/index.ts new file mode 100644 index 0000000000..2237c02d52 --- /dev/null +++ b/packages/app/src/model/Query/ProxiesQuery/index.ts @@ -0,0 +1,29 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ProxiesQuery { + #pApi: PapiApi; + + #address: string; + + constructor(pApi: PapiApi, address: string) { + this.#pApi = pApi; + this.#address = address; + } + + async fetch() { + try { + const result = await this.#pApi.query.Proxy.Proxies.getValue( + this.#address, + { at: 'best' } + ); + return result; + } catch (e) { + // Subscription failed. + } + + return undefined; + } +} diff --git a/packages/app/src/model/Query/SessionValidators/index.ts b/packages/app/src/model/Query/SessionValidators/index.ts new file mode 100644 index 0000000000..011d08f875 --- /dev/null +++ b/packages/app/src/model/Query/SessionValidators/index.ts @@ -0,0 +1,25 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class SessionValidators { + #pApi: PapiApi; + + constructor(pApi: PapiApi) { + this.#pApi = pApi; + } + + // Fetch network constants. + async fetch() { + try { + const result = await this.#pApi.query.Session.Validators.getValue({ + at: 'best', + }); + return result; + } catch (e) { + // Silently fail. + } + return []; + } +} diff --git a/packages/app/src/model/Query/StakingConstants/index.ts b/packages/app/src/model/Query/StakingConstants/index.ts deleted file mode 100644 index e68ff841e6..0000000000 --- a/packages/app/src/model/Query/StakingConstants/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import type { ApiPromise } from '@polkadot/api'; -import BigNumber from 'bignumber.js'; -import { NetworkList } from 'config/networks'; -import { stringToBn } from 'library/Utils'; - -export class StakingConstants { - // ------------------------------------------------------ - // Class members. - // ------------------------------------------------------ - - // Network config fallback values. - FALLBACK = { - MAX_NOMINATIONS: new BigNumber(16), - BONDING_DURATION: new BigNumber(28), - SESSIONS_PER_ERA: new BigNumber(6), - MAX_ELECTING_VOTERS: new BigNumber(22500), - EXPECTED_BLOCK_TIME: new BigNumber(6000), - EPOCH_DURATION: new BigNumber(2400), - }; - - // Fetch network constants. - async fetch(api: ApiPromise, network: string) { - const allPromises = [ - api.consts.staking.bondingDuration, - api.consts.staking.maxNominations, - api.consts.staking.sessionsPerEra, - api.consts.electionProviderMultiPhase.maxElectingVoters, - api.consts.babe.expectedBlockTime, - api.consts.babe.epochDuration, - api.consts.balances.existentialDeposit, - api.consts.staking.historyDepth, - api.consts.fastUnstake.deposit, - api.consts.nominationPools.palletId, - api.consts.staking.maxExposurePageSize, - ]; - - const consts = await Promise.all(allPromises); - - return { - bondDuration: consts[0] - ? stringToBn(consts[0].toString()) - : this.FALLBACK.BONDING_DURATION, - maxNominations: consts[1] - ? stringToBn(consts[1].toString()) - : this.FALLBACK.MAX_NOMINATIONS, - sessionsPerEra: consts[2] - ? stringToBn(consts[2].toString()) - : this.FALLBACK.SESSIONS_PER_ERA, - maxElectingVoters: consts[3] - ? stringToBn(consts[3].toString()) - : this.FALLBACK.MAX_ELECTING_VOTERS, - expectedBlockTime: consts[4] - ? stringToBn(consts[4].toString()) - : this.FALLBACK.EXPECTED_BLOCK_TIME, - epochDuration: consts[5] - ? stringToBn(consts[5].toString()) - : this.FALLBACK.EPOCH_DURATION, - existentialDeposit: consts[6] - ? stringToBn(consts[6].toString()) - : new BigNumber(0), - historyDepth: consts[7] - ? stringToBn(consts[7].toString()) - : new BigNumber(0), - fastUnstakeDeposit: consts[8] - ? stringToBn(consts[8].toString()) - : new BigNumber(0), - poolsPalletId: consts[9] ? consts[9].toU8a() : new Uint8Array(0), - maxExposurePageSize: consts[10] - ? stringToBn(consts[10].toString()) - : NetworkList[network].maxExposurePageSize, - }; - } -} diff --git a/packages/app/src/model/Query/SuperOfMulti/index.ts b/packages/app/src/model/Query/SuperOfMulti/index.ts new file mode 100644 index 0000000000..6f1c1d8f80 --- /dev/null +++ b/packages/app/src/model/Query/SuperOfMulti/index.ts @@ -0,0 +1,29 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class SuperOfMulti { + #pApi: PapiApi; + + #addresses: [string][]; + + constructor(pApi: PapiApi, addresses: [string][]) { + this.#pApi = pApi; + this.#addresses = addresses; + } + + async fetch() { + try { + const result = await this.#pApi.query.Identity.SuperOf.getValues( + this.#addresses, + { at: 'best' } + ); + return result; + } catch (e) { + // Silently fail. + } + + return null; + } +} diff --git a/packages/app/src/model/Query/ValidatorPrefs/index.ts b/packages/app/src/model/Query/ValidatorPrefs/index.ts new file mode 100644 index 0000000000..c80aa2c723 --- /dev/null +++ b/packages/app/src/model/Query/ValidatorPrefs/index.ts @@ -0,0 +1,31 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ValidatorPrefs { + #pApi: PapiApi; + #era: number; + #address: string; + + constructor(pApi: PapiApi, era: number, address: string) { + this.#pApi = pApi; + this.#era = era; + this.#address = address; + } + + async fetch() { + try { + const result = await this.#pApi.query.Staking.ErasValidatorPrefs.getValue( + this.#era, + this.#address, + { at: 'best' } + ); + return result; + } catch (e) { + // Silently fail. + } + + return undefined; + } +} diff --git a/packages/app/src/model/Query/ValidatorsMulti/index.ts b/packages/app/src/model/Query/ValidatorsMulti/index.ts new file mode 100644 index 0000000000..414037ecc9 --- /dev/null +++ b/packages/app/src/model/Query/ValidatorsMulti/index.ts @@ -0,0 +1,29 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ValidatorsMulti { + #pApi: PapiApi; + + #addresses: [string][]; + + constructor(pApi: PapiApi, addresses: [string][]) { + this.#pApi = pApi; + this.#addresses = addresses; + } + + async fetch() { + try { + const result = await this.#pApi.query.Staking.Validators.getValues( + this.#addresses, + { at: 'best' } + ); + return result; + } catch (e) { + // Silently fail. + } + + return null; + } +} diff --git a/packages/app/src/model/Subscribe/AccountBalances/index.ts b/packages/app/src/model/Subscribe/AccountBalances/index.ts new file mode 100644 index 0000000000..eaf52d1c98 --- /dev/null +++ b/packages/app/src/model/Subscribe/AccountBalances/index.ts @@ -0,0 +1,234 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import BigNumber from 'bignumber.js'; +import type { Balances, Ledger, Nominations } from 'contexts/Balances/types'; +import type { PoolMembership } from 'contexts/Pools/types'; +import type { PayeeConfig } from 'contexts/Setup/types'; +import { ApiController } from 'controllers/Api'; +import { BalancesController } from 'controllers/Balances'; +import { defaultNominations } from 'controllers/Balances/defaults'; +import type { Unsubscribable } from 'controllers/Subscriptions/types'; +import { stringToBn } from 'library/Utils'; +import type { PapiApi } from 'model/Api/types'; +import type { Subscription } from 'rxjs'; +import { combineLatest } from 'rxjs'; +import type { AnyApi, NetworkName } from 'types'; + +export class AccountBalances implements Unsubscribable { + // The associated network for this instance. + #network: NetworkName; + + // Active subscription. + #sub: Subscription; + + // Account to subscribe to. + #address: string; + + // Account ledger. + ledger: Ledger | undefined; + + // Account balances. + balance: Balances; + + // Payee config. + payee: PayeeConfig | undefined; + + // Pool membership. + poolMembership: PoolMembership | undefined; + + // Account nominations. + nominations: Nominations; + + constructor(network: NetworkName, address: string) { + this.#network = network; + this.#address = address; + this.subscribe(); + } + + subscribe = async (): Promise => { + try { + const { pApi } = ApiController.get(this.#network); + const bestOrFinalized = 'best'; + + if (pApi && this.#sub === undefined) { + const sub = combineLatest([ + pApi.query.Staking.Ledger.watchValue(this.#address, bestOrFinalized), + pApi.query.System.Account.watchValue(this.#address, bestOrFinalized), + pApi.query.Balances.Locks.watchValue(this.#address, bestOrFinalized), + pApi.query.Staking.Payee.watchValue(this.#address, bestOrFinalized), + pApi.query.NominationPools.PoolMembers.watchValue( + this.#address, + bestOrFinalized + ), + pApi.query.NominationPools.ClaimPermissions.watchValue( + this.#address, + bestOrFinalized + ), + pApi.query.Staking.Nominators.watchValue( + this.#address, + bestOrFinalized + ), + ]).subscribe( + async ([ + ledger, + account, + locks, + payee, + poolMembers, + claimPermissions, + nominators, + ]) => { + this.handleLedger(ledger); + this.handleAccount(account, locks); + this.handlePayee(payee); + + await this.handlePoolMembership( + pApi, + poolMembers, + claimPermissions + ); + this.handleNominations(nominators); + + // Send updated account state back to UI. + const accountBalance = { + address: this.#address, + ledger: this.ledger, + balances: this.balance, + payee: this.payee, + poolMembership: this.poolMembership, + nominations: this.nominations, + }; + + document.dispatchEvent( + new CustomEvent('new-account-balance', { detail: accountBalance }) + ); + } + ); + + this.#sub = sub; + } + } catch (e) { + // Subscription failed. + } + }; + + // Handle ledger result. + handleLedger = (ledger: AnyApi): void => { + // If ledger is null, remove from class. + if (!ledger) { + this.ledger = undefined; + } else { + const { stash, total, active, unlocking } = ledger; + + // Send stash address to UI as event if not presently imported. + if (!BalancesController.accounts.includes(stash.toString())) { + document.dispatchEvent( + new CustomEvent('new-external-account', { + detail: { address: stash.toString() }, + }) + ); + } + + this.ledger = { + stash: stash.toString(), + active: stringToBn(active.toString()), + total: stringToBn(total.toString()), + unlocking: unlocking.map( + ({ era, value }: { era: number; value: bigint }) => ({ + era: Number(era), + value: stringToBn(value.toString()), + }) + ), + }; + } + }; + + // Handle account callback. + handleAccount = ( + { data: accountData, nonce }: AnyApi, + locksResult: AnyApi + ): void => { + this.balance = { + nonce, + balance: { + free: stringToBn(accountData.free.toString()), + reserved: stringToBn(accountData.reserved.toString()), + frozen: stringToBn(accountData.frozen.toString()), + }, + locks: locksResult.map((lock: { id: AnyApi; amount: bigint }) => ({ + id: lock.id.asText().trim(), + amount: stringToBn(lock.amount.toString()), + })), + }; + }; + + // Handle payee callback. + handlePayee = (result: AnyApi): void => { + if (result === undefined) { + this.payee = undefined; + } else { + this.payee = { + destination: result.type || null, + account: result.value || undefined, + }; + } + }; + + // Handle pool membership and claim commission callback. + handlePoolMembership = async ( + pApi: PapiApi, + poolMembers: AnyApi, + claimPermissionResult: AnyApi + ): Promise => { + // If pool membership is `null`, remove pool membership data from class data and exit early. + // This skips claim permission data as well as user would not have claim permissions if they are + // not in a pool. + if (!poolMembers) { + this.poolMembership = undefined; + return; + } + + const unlocking = poolMembers?.unbonding_eras.map(([e, v]: AnyApi) => ({ + era: e, + value: new BigNumber((v as bigint).toString()), + })); + + const apiResult = await pApi.apis.NominationPoolsApi.points_to_balance( + poolMembers.pool_id, + poolMembers.points, + { at: 'best' } + ); + const balance = new BigNumber(apiResult?.toString() || 0); + const claimPermission = claimPermissionResult?.type || 'Permissioned'; + + this.poolMembership = { + address: this.#address, + poolId: poolMembers.pool_id, + points: poolMembers.points.toString(), + balance, + lastRecordedRewardCounter: + poolMembers.last_recorded_reward_counter.toString(), + unbondingEras: unlocking, // NOTE: This is a duplicate of `unlocking`. + claimPermission, + unlocking, + }; + }; + + // Handle nominations callback. + handleNominations = (nominators: AnyApi): void => { + this.nominations = !nominators + ? defaultNominations + : { + targets: nominators.targets, + submittedIn: nominators.submitted_in, + }; + }; + + // Unsubscribe from class subscription. + unsubscribe = (): void => { + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); + } + }; +} diff --git a/packages/app/src/model/Subscribe/AccountProxies/index.ts b/packages/app/src/model/Subscribe/AccountProxies/index.ts new file mode 100644 index 0000000000..b2688cace9 --- /dev/null +++ b/packages/app/src/model/Subscribe/AccountProxies/index.ts @@ -0,0 +1,57 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import { ApiController } from 'controllers/Api'; +import type { Unsubscribable } from 'controllers/Subscriptions/types'; +import type { Subscription } from 'rxjs'; +import type { AnyApi, NetworkName } from 'types'; + +export class AccountProxies implements Unsubscribable { + // The associated network for this instance. + #network: NetworkName; + + // The proxy delegator address. + #address: string; + + // The bonded address. + proxies: AnyApi; + + // Active subscription. + #sub: Subscription; + + constructor(network: NetworkName, address: string) { + this.#network = network; + this.#address = address; + this.subscribe(); + } + + subscribe = async (): Promise => { + try { + const { pApi } = ApiController.get(this.#network); + + if (pApi && this.#sub === undefined) { + const bestOrFinalized = 'best'; + const unsub = pApi.query.Proxy.Proxies.watchValue( + this.#address, + bestOrFinalized + ).subscribe((proxies) => { + document.dispatchEvent( + new CustomEvent('new-account-proxies', { + detail: { address: this.#address, proxies }, + }) + ); + }); + this.#sub = unsub; + } + } catch (e) { + // Subscription failed. + } + }; + + // Unsubscribe from class subscription. + unsubscribe = (): void => { + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); + } + }; +} diff --git a/packages/app/src/model/Subscribe/AccountProxies/types.ts b/packages/app/src/model/Subscribe/AccountProxies/types.ts new file mode 100644 index 0000000000..3e2f791555 --- /dev/null +++ b/packages/app/src/model/Subscribe/AccountProxies/types.ts @@ -0,0 +1,19 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +export type AccountProxy = [ + { + delay: number; + delegate: string; + proxy_type: { + type: string; + value: undefined; + }; + }[], + bigint, +]; + +export interface AccountProxiesEvent { + address: string; + proxies: AccountProxy; +} diff --git a/packages/app/src/model/Subscribe/ActiveEra/index.ts b/packages/app/src/model/Subscribe/ActiveEra/index.ts index 79f3f4848f..c0d6d61ed8 100644 --- a/packages/app/src/model/Subscribe/ActiveEra/index.ts +++ b/packages/app/src/model/Subscribe/ActiveEra/index.ts @@ -1,64 +1,45 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { VoidFn } from '@polkadot/api/types'; import BigNumber from 'bignumber.js'; import { defaultActiveEra } from 'contexts/Api/defaults'; import type { APIActiveEra } from 'contexts/Api/types'; import { ApiController } from 'controllers/Api'; import { SubscriptionsController } from 'controllers/Subscriptions'; import type { Unsubscribable } from 'controllers/Subscriptions/types'; -import type { AnyApi, NetworkName } from 'types'; +import type { Subscription } from 'rxjs'; +import type { NetworkName } from 'types'; import { StakingMetrics } from '../StakingMetrics'; export class ActiveEra implements Unsubscribable { - // ------------------------------------------------------ - // Class members. - // ------------------------------------------------------ - // The associated network for this instance. #network: NetworkName; - // Unsubscribe object. - #unsub: VoidFn; + // Active subscription. + #sub: Subscription; // Store the active era. activeEra: APIActiveEra = defaultActiveEra; - // ------------------------------------------------------ - // Constructor. - // ------------------------------------------------------ - constructor(network: NetworkName) { this.#network = network; - - // Subscribe immediately. this.subscribe(); } - // ------------------------------------------------------ - // Subscription. - // ------------------------------------------------------ - subscribe = async (): Promise => { try { - const { api } = ApiController.get(this.#network); - - if (api && this.#unsub === undefined) { - const unsub = await api.query.staking.activeEra((result: AnyApi) => { - // determine activeEra: toString used as alternative to `toHuman`, that puts commas in - // numbers - const activeEra = JSON.parse(result.unwrapOrDefault().toString()); - - // Return early if errornous active era is returned. - if (activeEra.index === 0 || !activeEra.start) { - return; - } - + const { pApi } = ApiController.get(this.#network); + + if (pApi && this.#sub === undefined) { + // Testing the active era subscription. + const bestOrFinalized = 'best'; + const sub = pApi.query.Staking.ActiveEra.watchValue( + bestOrFinalized + ).subscribe((activeEra) => { // Store active era. this.activeEra = { - index: new BigNumber(activeEra.index), - start: new BigNumber(activeEra.start), + index: new BigNumber(activeEra?.index.toString() || 0), + start: new BigNumber(activeEra?.start?.toString() || 0), }; // Unsubscribe to staking metrics if it exists. @@ -66,6 +47,7 @@ export class ActiveEra implements Unsubscribable { this.#network, 'stakingMetrics' ); + if (subStakingMetrics) { subStakingMetrics.subscribe(); SubscriptionsController.remove(this.#network, 'stakingMetrics'); @@ -88,23 +70,17 @@ export class ActiveEra implements Unsubscribable { }) ); }); - - // Subscription now initialised. Store unsub. - this.#unsub = unsub as unknown as VoidFn; + this.#sub = sub; } } catch (e) { - // Block number subscription failed. + // Subscription failed. } }; - // ------------------------------------------------------ - // Unsubscribe handler. - // ------------------------------------------------------ - // Unsubscribe from class subscription. unsubscribe = (): void => { - if (typeof this.#unsub === 'function') { - this.#unsub(); + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); } }; } diff --git a/packages/app/src/model/Subscribe/ActivePoolAccount/index.ts b/packages/app/src/model/Subscribe/ActivePoolAccount/index.ts new file mode 100644 index 0000000000..2242dab0bb --- /dev/null +++ b/packages/app/src/model/Subscribe/ActivePoolAccount/index.ts @@ -0,0 +1,177 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { Nominations } from 'contexts/Balances/types'; +import { defaultPoolNominations } from 'contexts/Pools/ActivePool/defaults'; +import type { ActivePool, PoolRoles } from 'contexts/Pools/ActivePool/types'; +import type { ActivePoolItem } from 'controllers/ActivePools/types'; +import { ApiController } from 'controllers/Api'; +import { IdentitiesController } from 'controllers/Identities'; +import type { Unsubscribable } from 'controllers/Subscriptions/types'; +import type { PapiApi } from 'model/Api/types'; +import { combineLatest, type Subscription } from 'rxjs'; +import type { AnyApi, NetworkName, SystemChainId } from 'types'; + +export class ActivePoolAccount implements Unsubscribable { + // The associated network for this instance. + #network: NetworkName | SystemChainId; + + // Active subscription. + #sub: Subscription; + + // Active pool item + pool: ActivePoolItem; + + // Address associated with this pool. + address: string; + + // Active pool of the address. + activePool: ActivePool | null; + + // Active pool nominations. + poolNominations: Nominations; + + constructor( + network: NetworkName | SystemChainId, + address: string, + pool: ActivePoolItem + ) { + this.#network = network; + this.pool = pool; + this.address = address; + this.subscribe(); + } + + subscribe = async (): Promise => { + try { + const { pApi } = ApiController.get(this.#network); + const { pApi: peopleApi } = ApiController.get( + `people-${this.#network}` as SystemChainId + ); + const bestOrFinalized = 'best'; + + const sub = combineLatest([ + pApi.query.NominationPools.BondedPools.watchValue( + Number(this.pool.id), + bestOrFinalized + ), + pApi.query.NominationPools.RewardPools.watchValue( + Number(this.pool.id), + bestOrFinalized + ), + pApi.query.System.Account.watchValue( + this.pool.addresses.reward, + bestOrFinalized + ), + pApi.query.Staking.Nominators.watchValue( + this.pool.addresses.stash, + bestOrFinalized + ), + ]).subscribe(async ([bondedPool, rewardPool, account, nominators]) => { + await this.handleActivePoolCallback( + peopleApi, + bondedPool, + rewardPool, + account + ); + this.handleNominatorsCallback(nominators); + + if (this.activePool && this.poolNominations) { + document.dispatchEvent( + new CustomEvent('new-active-pool', { + detail: { + address: this.address, + pool: this.activePool, + nominations: this.poolNominations, + }, + }) + ); + } + }); + + this.#sub = sub; + } catch (e) { + // Subscription failed. + } + }; + + // Handle active pool callback. + handleActivePoolCallback = async ( + peopleApi: PapiApi, + bondedPool: AnyApi, + rewardPool: AnyApi, + account: AnyApi + ): Promise => { + const balance = account.data; + const rewardAccountBalance = balance?.free.toString(); + + if (peopleApi) { + // Fetch identities for roles and expand `bondedPool` state to store them. + bondedPool.roleIdentities = await IdentitiesController.fetch( + peopleApi, + this.getUniqueRoleAddresses(bondedPool.roles) + ); + } + + const bondedPoolFormatted = { + points: bondedPool.points.toString(), + memberCounter: bondedPool.member_counter.toString(), + roles: bondedPool.roles, + roleIdentities: bondedPool.roleIdentities, + state: bondedPool.state.type, + }; + + const rewardPoolFormatted = { + lastRecordedRewardCounter: + rewardPool.last_recorded_reward_counter.toString(), + lastRecordedTotalPayouts: + rewardPool.last_recorded_total_payouts.toString(), + totalCommissionClaimed: rewardPool.total_commission_claimed.toString(), + totalCommissionPending: rewardPool.total_commission_pending.toString(), + totalRewardsClaimed: rewardPool.total_rewards_claimed.toString(), + }; + + // Only persist the active pool to class state (and therefore dispatch an event) if both the + // bonded pool and reward pool are returned. + if (bondedPool && rewardPool) { + const newPool = { + id: Number(this.pool.id), + addresses: this.pool.addresses, + bondedPool: bondedPoolFormatted, + rewardPool: rewardPoolFormatted, + rewardAccountBalance, + }; + + this.activePool = newPool; + } else { + // Invalid pools were returned. To signal pool was synced, set active pool to `null`. + this.activePool = null; + } + }; + + // Handle nominators callback. + handleNominatorsCallback = (nominators: AnyApi): void => { + const newNominations: Nominations = !nominators + ? defaultPoolNominations + : { + targets: nominators.targets, + submittedIn: nominators.submitted_in, + }; + this.poolNominations = newNominations; + }; + + // Gets unique role addresses from a bonded pool's `roles` record. + getUniqueRoleAddresses = (roles: PoolRoles): string[] => { + const roleAddresses: string[] = [ + ...new Set(Object.values(roles).filter((role) => role !== undefined)), + ]; + return roleAddresses; + }; + + // Unsubscribe from class subscription. + unsubscribe = (): void => { + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); + } + }; +} diff --git a/packages/app/src/model/Subscribe/BlockNumber/index.ts b/packages/app/src/model/Subscribe/BlockNumber/index.ts index 69a4711164..13d48f8e90 100644 --- a/packages/app/src/model/Subscribe/BlockNumber/index.ts +++ b/packages/app/src/model/Subscribe/BlockNumber/index.ts @@ -1,47 +1,35 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { VoidFn } from '@polkadot/api/types'; import { ApiController } from 'controllers/Api'; import type { Unsubscribable } from 'controllers/Subscriptions/types'; +import type { Subscription } from 'rxjs'; import type { NetworkName } from 'types'; export class BlockNumber implements Unsubscribable { - // ------------------------------------------------------ - // Class members. - // ------------------------------------------------------ - // The associated network for this instance. #network: NetworkName; // The current block number. blockNumber = '0'; - // Unsubscribe object. - #unsub: VoidFn; - - // ------------------------------------------------------ - // Constructor. - // ------------------------------------------------------ + // Active subscription. + #sub: Subscription; constructor(network: NetworkName) { this.#network = network; - - // Subscribe immediately. this.subscribe(); } - // ------------------------------------------------------ - // Subscription. - // ------------------------------------------------------ - subscribe = async (): Promise => { try { - const { api } = ApiController.get(this.#network); + const { pApi } = ApiController.get(this.#network); - if (api && this.#unsub === undefined) { - // Get block numbers. - const unsub = await api.query.system.number((num: number) => { + if (pApi && this.#sub === undefined) { + const bestOrFinalized = 'best'; + const unsub = pApi.query.System.Number.watchValue( + bestOrFinalized + ).subscribe((num) => { // Update class block number. this.blockNumber = num.toString(); @@ -54,23 +42,17 @@ export class BlockNumber implements Unsubscribable { }) ); }); - - // Subscription now initialised. Store unsub. - this.#unsub = unsub as unknown as VoidFn; + this.#sub = unsub; } } catch (e) { - // Block number subscription failed. + // Subscription failed. } }; - // ------------------------------------------------------ - // Unsubscribe handler. - // ------------------------------------------------------ - // Unsubscribe from class subscription. unsubscribe = (): void => { - if (typeof this.#unsub === 'function') { - this.#unsub(); + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); } }; } diff --git a/packages/app/src/model/Subscribe/Bonded/index.tsx b/packages/app/src/model/Subscribe/Bonded/index.tsx new file mode 100644 index 0000000000..6a886a2cee --- /dev/null +++ b/packages/app/src/model/Subscribe/Bonded/index.tsx @@ -0,0 +1,66 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { BondedAccount } from 'contexts/Bonded/types'; +import { ApiController } from 'controllers/Api'; +import type { Unsubscribable } from 'controllers/Subscriptions/types'; +import type { Subscription } from 'rxjs'; +import type { NetworkName } from 'types'; + +export class Bonded implements Unsubscribable { + // The associated network for this instance. + #network: NetworkName; + + // The stash address. + #address: string; + + // The bonded address. + bonded: string; + + // Active subscription. + #sub: Subscription; + + constructor(network: NetworkName, address: string) { + this.#network = network; + this.#address = address; + this.subscribe(); + } + + subscribe = async (): Promise => { + try { + const { pApi } = ApiController.get(this.#network); + + if (pApi && this.#sub === undefined) { + const bestOrFinalized = 'best'; + const unsub = pApi.query.Staking.Bonded.watchValue( + this.#address, + bestOrFinalized + ).subscribe((controller) => { + const account: BondedAccount = { + address: this.#address, + bonded: controller || undefined, + }; + + // Send bonded account to UI. + document.dispatchEvent( + new CustomEvent('new-bonded-account', { + detail: { + account, + }, + }) + ); + }); + this.#sub = unsub; + } + } catch (e) { + // Subscription failed. + } + }; + + // Unsubscribe from class subscription. + unsubscribe = (): void => { + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); + } + }; +} diff --git a/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts b/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts new file mode 100644 index 0000000000..202c791b28 --- /dev/null +++ b/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts @@ -0,0 +1,66 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import { ApiController } from 'controllers/Api'; +import type { Unsubscribable } from 'controllers/Subscriptions/types'; +import type { Subscription } from 'rxjs'; +import { combineLatest } from 'rxjs'; +import type { NetworkName } from 'types'; +import type { FastUnstakeConfigResult } from './types'; + +export class FastUnstakeConfig implements Unsubscribable { + // The associated network for this instance. + #network: NetworkName; + + // Active subscription. + #sub: Subscription; + + config: FastUnstakeConfigResult; + + constructor(network: NetworkName) { + this.#network = network; + this.subscribe(); + } + + subscribe = async (): Promise => { + try { + const { pApi } = ApiController.get(this.#network); + + if (pApi && this.#sub === undefined) { + const bestOrFinalized = 'best'; + + const sub = combineLatest([ + pApi.query.FastUnstake.Head.watchValue(bestOrFinalized), + pApi.query.FastUnstake.CounterForQueue.watchValue(bestOrFinalized), + ]).subscribe(([head, counterForQueue]) => { + const config: FastUnstakeConfigResult = { + head: head || { + stashes: [], + checked: [], + }, + counterForQueue, + }; + + this.config = config; + + document.dispatchEvent( + new CustomEvent('new-fast-unstake-config', { + detail: { ...config }, + }) + ); + }); + + this.#sub = sub; + } + } catch (e) { + // Subscription failed. + } + }; + + // Unsubscribe from class subscription. + unsubscribe = (): void => { + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); + } + }; +} diff --git a/packages/app/src/model/Subscribe/FastUnstakeConfig/types.ts b/packages/app/src/model/Subscribe/FastUnstakeConfig/types.ts new file mode 100644 index 0000000000..8d64f3c59f --- /dev/null +++ b/packages/app/src/model/Subscribe/FastUnstakeConfig/types.ts @@ -0,0 +1,12 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +export interface FastUnstakeConfigResult { + head: FastUnstakeHead; + counterForQueue: number; +} + +export interface FastUnstakeHead { + stashes: [string, bigint][]; + checked: number[]; +} diff --git a/packages/app/src/model/Subscribe/FastUnstakeQueue/index.ts b/packages/app/src/model/Subscribe/FastUnstakeQueue/index.ts new file mode 100644 index 0000000000..8bd43e81d9 --- /dev/null +++ b/packages/app/src/model/Subscribe/FastUnstakeQueue/index.ts @@ -0,0 +1,62 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import { ApiController } from 'controllers/Api'; +import type { Unsubscribable } from 'controllers/Subscriptions/types'; +import type { Subscription } from 'rxjs'; +import type { NetworkName } from 'types'; + +export class FastUnstakeQueue implements Unsubscribable { + // The associated network for this instance. + #network: NetworkName; + + // The depositor address. + #address: string; + + // The deposit. + queue: bigint; + + // Active subscription. + #sub: Subscription; + + constructor(network: NetworkName, address: string) { + this.#network = network; + this.#address = address; + this.subscribe(); + } + + subscribe = async (): Promise => { + try { + const { pApi } = ApiController.get(this.#network); + + if (pApi && this.#sub === undefined) { + const bestOrFinalized = 'best'; + const unsub = pApi.query.FastUnstake.Queue.watchValue( + this.#address, + bestOrFinalized + ).subscribe((queue) => { + this.queue = queue || 0n; + + document.dispatchEvent( + new CustomEvent('new-fast-unstake-deposit', { + detail: { + address: this.#address, + deposit: queue || 0n, + }, + }) + ); + }); + this.#sub = unsub; + } + } catch (e) { + // Subscription failed. + } + }; + + // Unsubscribe from class subscription. + unsubscribe = (): void => { + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); + } + }; +} diff --git a/packages/app/src/model/Subscribe/NetworkMetrics/index.ts b/packages/app/src/model/Subscribe/NetworkMetrics/index.ts index e888747087..20834c4604 100644 --- a/packages/app/src/model/Subscribe/NetworkMetrics/index.ts +++ b/packages/app/src/model/Subscribe/NetworkMetrics/index.ts @@ -1,61 +1,55 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { VoidFn } from '@polkadot/api/types'; -import { rmCommas } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import { ApiController } from 'controllers/Api'; import type { Unsubscribable } from 'controllers/Subscriptions/types'; +import type { Subscription } from 'rxjs'; +import { combineLatest } from 'rxjs'; import type { NetworkName } from 'types'; export class NetworkMetrics implements Unsubscribable { - // ------------------------------------------------------ - // Class members. - // ------------------------------------------------------ - // The associated network for this instance. #network: NetworkName; - // Unsubscribe object. - #unsub: VoidFn; - - // ------------------------------------------------------ - // Constructor. - // ------------------------------------------------------ + // Active subscription. + #sub: Subscription; constructor(network: NetworkName) { this.#network = network; - - // Subscribe immediately. this.subscribe(); } - // ------------------------------------------------------ - // Subscription. - // ------------------------------------------------------ - subscribe = async (): Promise => { try { - const { api } = ApiController.get(this.#network); + const { pApi } = ApiController.get(this.#network); - if (api && this.#unsub === undefined) { - const unsub = await api.queryMulti( - [ - api.query.balances.totalIssuance, - api.query.auctions.auctionCounter, - api.query.paraSessionInfo.earliestStoredSession, - api.query.fastUnstake.erasToCheckPerBlock, - api.query.staking.minimumActiveStake, - ], - (result) => { + if (pApi && this.#sub === undefined) { + const bestOrFinalized = 'best'; + const sub = combineLatest([ + pApi.query.Balances.TotalIssuance.watchValue(bestOrFinalized), + pApi.query.Auctions.AuctionCounter.watchValue(bestOrFinalized), + pApi.query.ParaSessionInfo.EarliestStoredSession.watchValue( + bestOrFinalized + ), + pApi.query.FastUnstake.ErasToCheckPerBlock.watchValue( + bestOrFinalized + ), + pApi.query.Staking.MinimumActiveStake.watchValue(bestOrFinalized), + ]).subscribe( + ([ + totalIssuance, + auctionCounter, + earliestStoredSession, + erasToCheckPerBlock, + minimumActiveStake, + ]) => { const networkMetrics = { - totalIssuance: new BigNumber(result[0].toString()), - auctionCounter: new BigNumber(result[1].toString()), - earliestStoredSession: new BigNumber(result[2].toString()), - fastUnstakeErasToCheckPerBlock: Number( - rmCommas(result[3].toString()) - ), - minimumActiveStake: new BigNumber(result[4].toString()), + totalIssuance: new BigNumber(totalIssuance.toString()), + auctionCounter: new BigNumber(auctionCounter), + earliestStoredSession: new BigNumber(earliestStoredSession), + fastUnstakeErasToCheckPerBlock: Number(erasToCheckPerBlock), + minimumActiveStake: new BigNumber(minimumActiveStake.toString()), }; document.dispatchEvent( @@ -66,22 +60,17 @@ export class NetworkMetrics implements Unsubscribable { } ); - // Subscription now initialised. Store unsub. - this.#unsub = unsub as unknown as VoidFn; + this.#sub = sub; } } catch (e) { - // Block number subscription failed. + // Subscription failed. } }; - // ------------------------------------------------------ - // Unsubscribe handler. - // ------------------------------------------------------ - // Unsubscribe from class subscription. unsubscribe = (): void => { - if (typeof this.#unsub === 'function') { - this.#unsub(); + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); } }; } diff --git a/packages/app/src/model/Subscribe/PoolMembersMulti/index.ts b/packages/app/src/model/Subscribe/PoolMembersMulti/index.ts new file mode 100644 index 0000000000..fa922a9d25 --- /dev/null +++ b/packages/app/src/model/Subscribe/PoolMembersMulti/index.ts @@ -0,0 +1,92 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import { ApiController } from 'controllers/Api'; +import type { Unsubscribable } from 'controllers/Subscriptions/types'; +import { combineLatest, type Subscription } from 'rxjs'; +import type { AnyApi, NetworkName } from 'types'; +import type { PoolMemberBatchEvent } from './types'; + +export class PoolMembersMulti implements Unsubscribable { + // The associated network for this instance. + #network: NetworkName; + + // The batch key. + #key: string; + #addresses: string[]; + result: AnyApi; + + // Active subscription. + #sub: Subscription; + + constructor(network: NetworkName, key: string, addresses: string[]) { + this.#network = network; + this.#key = key; + this.#addresses = addresses; + this.subscribe(); + } + + subscribe = async (): Promise => { + try { + const { pApi } = ApiController.get(this.#network); + if (pApi && this.#sub === undefined) { + const bestOrFinalized = 'best'; + + const sub = combineLatest( + this.#addresses.map((address) => + pApi.query.NominationPools.PoolMembers.watchValue( + address, + bestOrFinalized + ) + ) + ).subscribe((results) => { + const formatted = results + .map((result) => { + if (!result) { + return undefined; + } + + return { + lastRecordedRewardCounter: + result.last_recorded_reward_counter.toString(), + points: result.points.toString(), + poolId: result.pool_id.toString(), + unbondingEras: Object.fromEntries( + result.unbonding_eras.map( + ([key, value]: [number, bigint]) => [ + key.toString(), + value.toString(), + ] + ) + ), + }; + }) + .filter((result) => result !== undefined); + + const detail: PoolMemberBatchEvent = { + key: this.#key, + addresses: this.#addresses, + poolMembers: formatted, + }; + + document.dispatchEvent( + new CustomEvent('new-pool-members-batch', { + detail, + }) + ); + }); + + this.#sub = sub; + } + } catch (e) { + // Subscription failed. + } + }; + + // Unsubscribe from class subscription. + unsubscribe = (): void => { + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); + } + }; +} diff --git a/packages/app/src/model/Subscribe/PoolMembersMulti/types.ts b/packages/app/src/model/Subscribe/PoolMembersMulti/types.ts new file mode 100644 index 0000000000..ed7b855149 --- /dev/null +++ b/packages/app/src/model/Subscribe/PoolMembersMulti/types.ts @@ -0,0 +1,15 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +export interface PoolMemberBatchEvent { + key: string; + addresses: string[]; + poolMembers: Record< + number, + { + poolId: string; + points: string; + unbondingEras: Record; + } + >; +} diff --git a/packages/app/src/model/Subscribe/PoolsConfig/index.ts b/packages/app/src/model/Subscribe/PoolsConfig/index.ts index 97e4ffae33..528ca27fd6 100644 --- a/packages/app/src/model/Subscribe/PoolsConfig/index.ts +++ b/packages/app/src/model/Subscribe/PoolsConfig/index.ts @@ -1,28 +1,20 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { VoidFn } from '@polkadot/api/types'; -import { rmCommas } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import { ApiController } from 'controllers/Api'; import type { Unsubscribable } from 'controllers/Subscriptions/types'; import { stringToBn } from 'library/Utils'; +import type { Subscription } from 'rxjs'; +import { combineLatest } from 'rxjs'; import type { NetworkName } from 'types'; export class PoolsConfig implements Unsubscribable { - // ------------------------------------------------------ - // Class members. - // ------------------------------------------------------ - // The associated network for this instance. #network: NetworkName; - // Unsubscribe object. - #unsub: VoidFn; - - // ------------------------------------------------------ - // Constructor. - // ------------------------------------------------------ + // Active subscription. + #sub: Subscription; constructor(network: NetworkName) { this.#network = network; @@ -31,54 +23,84 @@ export class PoolsConfig implements Unsubscribable { this.subscribe(); } - // ------------------------------------------------------ - // Subscription. - // ------------------------------------------------------ - subscribe = async (): Promise => { try { - const { api } = ApiController.get(this.#network); - - if (api && this.#unsub === undefined) { - const unsub = await api.queryMulti( - [ - api.query.nominationPools.counterForPoolMembers, - api.query.nominationPools.counterForBondedPools, - api.query.nominationPools.counterForRewardPools, - api.query.nominationPools.lastPoolId, - api.query.nominationPools.maxPoolMembers, - api.query.nominationPools.maxPoolMembersPerPool, - api.query.nominationPools.maxPools, - api.query.nominationPools.minCreateBond, - api.query.nominationPools.minJoinBond, - api.query.nominationPools.globalMaxCommission, - ], - (result) => { - // format optional configs to BigNumber or null. - const maxPoolMembers = result[4].toHuman() - ? new BigNumber(rmCommas(result[4].toString())) + const { pApi } = ApiController.get(this.#network); + + if (pApi && this.#sub === undefined) { + const bestOrFinalized = 'best'; + const sub = combineLatest([ + pApi.query.NominationPools.CounterForPoolMembers.watchValue( + bestOrFinalized + ), + pApi.query.NominationPools.CounterForBondedPools.watchValue( + bestOrFinalized + ), + pApi.query.NominationPools.CounterForRewardPools.watchValue( + bestOrFinalized + ), + pApi.query.NominationPools.LastPoolId.watchValue(bestOrFinalized), + pApi.query.NominationPools.MaxPoolMembers.watchValue(bestOrFinalized), + pApi.query.NominationPools.MaxPoolMembersPerPool.watchValue( + bestOrFinalized + ), + pApi.query.NominationPools.MaxPools.watchValue(bestOrFinalized), + pApi.query.NominationPools.MinCreateBond.watchValue(bestOrFinalized), + pApi.query.NominationPools.MinJoinBond.watchValue(bestOrFinalized), + pApi.query.NominationPools.GlobalMaxCommission.watchValue( + bestOrFinalized + ), + ]).subscribe( + ([ + counterForPoolMembers, + counterForBondedPools, + counterForRewardPools, + lastPoolId, + maxPoolMembersRaw, + maxPoolMembersPerPoolRaw, + maxPoolsRaw, + minCreateBond, + minJoinBond, + globalMaxCommission, + ]) => { + // Format globalMaxCommission from a perbill to a percent. + const globalMaxCommissionAsPercent = globalMaxCommission + ? BigInt(globalMaxCommission) / 1000000n + : 100n; + + // Format max pool members to be a BigNumber, or null if it's not set. + const maxPoolMembers = maxPoolMembersRaw + ? new BigNumber(maxPoolMembersRaw.toString()) : null; - const maxPoolMembersPerPool = result[5].toHuman() - ? new BigNumber(rmCommas(result[5].toString())) + // Format max pool members per pool to be a BigNumber, or null if it's not set. + const maxPoolMembersPerPool = maxPoolMembersPerPoolRaw + ? new BigNumber(maxPoolMembersPerPoolRaw.toString()) : null; - const maxPools = result[6].toHuman() - ? new BigNumber(rmCommas(result[6].toString())) + // Format max pools to be a BigNumber, or null if it's not set. + const maxPools = maxPoolsRaw + ? new BigNumber(maxPoolsRaw.toString()) : null; const poolsConfig = { - counterForPoolMembers: stringToBn(result[0].toString()), - counterForBondedPools: stringToBn(result[1].toString()), - counterForRewardPools: stringToBn(result[2].toString()), - lastPoolId: stringToBn(result[3].toString()), + counterForPoolMembers: stringToBn( + counterForPoolMembers.toString() + ), + counterForBondedPools: stringToBn( + counterForBondedPools.toString() + ), + counterForRewardPools: stringToBn( + counterForRewardPools.toString() + ), + lastPoolId: stringToBn(lastPoolId.toString()), maxPoolMembers, maxPoolMembersPerPool, maxPools, - minCreateBond: stringToBn(result[7].toString()), - minJoinBond: stringToBn(result[8].toString()), + minCreateBond: stringToBn(minCreateBond.toString()), + minJoinBond: stringToBn(minJoinBond.toString()), globalMaxCommission: Number( - String(result[9]?.toHuman() || '100%').slice(0, -1) + globalMaxCommissionAsPercent.toString() ), }; @@ -89,23 +111,17 @@ export class PoolsConfig implements Unsubscribable { ); } ); - - // Subscription now initialised. Store unsub. - this.#unsub = unsub as unknown as VoidFn; + this.#sub = sub; } } catch (e) { // Subscription failed. } }; - // ------------------------------------------------------ - // Unsubscribe handler. - // ------------------------------------------------------ - // Unsubscribe from class subscription. unsubscribe = (): void => { - if (typeof this.#unsub === 'function') { - this.#unsub(); + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); } }; } diff --git a/packages/app/src/model/Subscribe/StakingMetrics/index.ts b/packages/app/src/model/Subscribe/StakingMetrics/index.ts index 83ca0f56b0..b9fff1160f 100644 --- a/packages/app/src/model/Subscribe/StakingMetrics/index.ts +++ b/packages/app/src/model/Subscribe/StakingMetrics/index.ts @@ -1,19 +1,16 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { VoidFn } from '@polkadot/api/types'; import type BigNumber from 'bignumber.js'; import type { APIActiveEra } from 'contexts/Api/types'; import { ApiController } from 'controllers/Api'; import type { Unsubscribable } from 'controllers/Subscriptions/types'; import { stringToBn } from 'library/Utils'; +import type { Subscription } from 'rxjs'; +import { combineLatest } from 'rxjs'; import type { NetworkName } from 'types'; export class StakingMetrics implements Unsubscribable { - // ------------------------------------------------------ - // Class members. - // ------------------------------------------------------ - // The associated network for this instance. #network: NetworkName; @@ -21,12 +18,8 @@ export class StakingMetrics implements Unsubscribable { #previousEra: BigNumber; - // Unsubscribe object. - #unsub: VoidFn; - - // ------------------------------------------------------ - // Constructor. - // ------------------------------------------------------ + // Active subscription. + #sub: Subscription; constructor( network: NetworkName, @@ -41,44 +34,52 @@ export class StakingMetrics implements Unsubscribable { this.subscribe(); } - // ------------------------------------------------------ - // Subscription. - // ------------------------------------------------------ - subscribe = async (): Promise => { try { - const { api } = ApiController.get(this.#network); + const { pApi } = ApiController.get(this.#network); - if (api && this.#unsub === undefined) { - const unsub = await api.queryMulti( - [ - api.query.staking.counterForNominators, - api.query.staking.counterForValidators, - api.query.staking.maxValidatorsCount, - api.query.staking.validatorCount, - [ - api.query.staking.erasValidatorReward, - this.#previousEra.toString(), - ], - [api.query.staking.erasTotalStake, this.#previousEra.toString()], - api.query.staking.minNominatorBond, - [ - api.query.staking.erasTotalStake, - this.#activeEra.index.toString(), - ], - api.query.staking.counterForNominators, - ], - (result) => { + if (pApi && this.#sub === undefined) { + const bestOrFinalized = 'best'; + const sub = combineLatest([ + pApi.query.Staking.CounterForValidators.watchValue(bestOrFinalized), + pApi.query.Staking.MaxValidatorsCount.watchValue(bestOrFinalized), + pApi.query.Staking.ValidatorCount.watchValue(bestOrFinalized), + pApi.query.Staking.ErasValidatorReward.watchValue( + this.#previousEra.toNumber(), + bestOrFinalized + ), + pApi.query.Staking.ErasTotalStake.watchValue( + this.#previousEra.toNumber(), + bestOrFinalized + ), + pApi.query.Staking.MinNominatorBond.watchValue(bestOrFinalized), + pApi.query.Staking.ErasTotalStake.watchValue( + this.#activeEra.index.toNumber(), + bestOrFinalized + ), + pApi.query.Staking.CounterForNominators.watchValue(bestOrFinalized), + ]).subscribe( + ([ + counterForValidators, + maxValidatorsCount, + validatorCount, + erasValidatorReward, + lastTotalStake, + minNominatorBond, + totalStaked, + counterForNominators, + ]) => { const stakingMetrics = { - totalNominators: stringToBn(result[0].toString()), - totalValidators: stringToBn(result[1].toString()), - maxValidatorsCount: stringToBn(result[2].toString()), - validatorCount: stringToBn(result[3].toString()), - lastReward: stringToBn(result[4].toString()), - lastTotalStake: stringToBn(result[5].toString()), - minNominatorBond: stringToBn(result[6].toString()), - totalStaked: stringToBn(result[7].toString()), - counterForNominators: stringToBn(result[8].toString()), + totalValidators: stringToBn(counterForValidators.toString()), + maxValidatorsCount: stringToBn( + maxValidatorsCount?.toString() || '0' + ), + validatorCount: stringToBn(validatorCount.toString()), + lastReward: stringToBn(erasValidatorReward?.toString() || '0'), + lastTotalStake: stringToBn(lastTotalStake.toString()), + minNominatorBond: stringToBn(minNominatorBond.toString()), + totalStaked: stringToBn(totalStaked.toString()), + counterForNominators: stringToBn(counterForNominators.toString()), }; document.dispatchEvent( @@ -88,22 +89,17 @@ export class StakingMetrics implements Unsubscribable { ); } ); - // Subscription now initialised. Store unsub. - this.#unsub = unsub as unknown as VoidFn; + this.#sub = sub; } } catch (e) { - // Block number subscription failed. + // Subscription failed. } }; - // ------------------------------------------------------ - // Unsubscribe handler. - // ------------------------------------------------------ - // Unsubscribe from class subscription. unsubscribe = (): void => { - if (typeof this.#unsub === 'function') { - this.#unsub(); + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); } }; } diff --git a/packages/app/src/pages/Overview/NetworkSats/index.tsx b/packages/app/src/pages/Overview/NetworkSats/index.tsx index 663d4bf280..a7517c64bb 100644 --- a/packages/app/src/pages/Overview/NetworkSats/index.tsx +++ b/packages/app/src/pages/Overview/NetworkSats/index.tsx @@ -15,7 +15,7 @@ export const NetworkStats = () => { const { t } = useTranslation('pages'); const { bondedPools } = useBondedPools(); const { getAverageRewardRate } = useAverageRewardRate(); - const { totalNominators, totalValidators } = useApi().stakingMetrics; + const { counterForNominators, totalValidators } = useApi().stakingMetrics; const { inflationToStakers } = getAverageRewardRate(false); @@ -27,7 +27,7 @@ export const NetworkStats = () => { }, { label: t('overview.totalNominators'), - value: totalNominators.toFormat(0), + value: counterForNominators.toFormat(0), helpKey: 'Total Nominators', }, { diff --git a/packages/app/src/pages/Pools/Home/PoolStats/index.tsx b/packages/app/src/pages/Pools/Home/PoolStats/index.tsx index 99aed174ce..5aaf34d4c1 100644 --- a/packages/app/src/pages/Pools/Home/PoolStats/index.tsx +++ b/packages/app/src/pages/Pools/Home/PoolStats/index.tsx @@ -4,6 +4,7 @@ import { rmCommas } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import { useNetwork } from 'contexts/Network'; +import { usePlugins } from 'contexts/Plugins'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { usePoolCommission } from 'hooks/usePoolCommission'; import { useOverlay } from 'kits/Overlay/Provider'; @@ -21,6 +22,7 @@ export const PoolStats = () => { const { networkData: { units, unit }, } = useNetwork(); + const { pluginEnabled } = usePlugins(); const { activePool } = useActivePool(); const { getCurrentCommission } = usePoolCommission(); @@ -67,13 +69,15 @@ export const PoolStats = () => { { label: t('pools.poolMembers'), value: `${memberCounter}`, - button: { - text: t('pools.browseMembers'), - onClick: () => { - openCanvas({ key: 'PoolMembers', size: 'xl' }); - }, - disabled: memberCounter === '0', - }, + button: pluginEnabled('subscan') + ? { + text: t('pools.browseMembers'), + onClick: () => { + openCanvas({ key: 'PoolMembers', size: 'xl' }); + }, + disabled: memberCounter === '0', + } + : undefined, }, { label: t('pools.totalBonded'), diff --git a/packages/app/src/pages/Pools/Home/index.tsx b/packages/app/src/pages/Pools/Home/index.tsx index c1da078062..4870ba304f 100644 --- a/packages/app/src/pages/Pools/Home/index.tsx +++ b/packages/app/src/pages/Pools/Home/index.tsx @@ -1,15 +1,10 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useBalances } from 'contexts/Balances'; import { useNetwork } from 'contexts/Network'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { useFavoritePools } from 'contexts/Pools/FavoritePools'; -import { useActivePools } from 'hooks/useActivePools'; -import { useSyncing } from 'hooks/useSyncing'; -import { useOverlay } from 'kits/Overlay/Provider'; import { CardWrapper } from 'library/Card/Wrappers'; import { PoolList } from 'library/PoolList'; import { PoolListProvider } from 'library/PoolList/context'; @@ -34,23 +29,9 @@ export const HomeInner = () => { const { t } = useTranslation('pages'); const { network } = useNetwork(); const { favorites } = useFavoritePools(); - const { openModal } = useOverlay().modal; const { bondedPools } = useBondedPools(); - const { getPoolMembership } = useBalances(); - const { poolMembersipSyncing } = useSyncing(); - const { activeAccount } = useActiveAccounts(); const { activeTab, setActiveTab } = usePoolsTabs(); const { getPoolRoles, activePool } = useActivePool(); - const membership = getPoolMembership(activeAccount); - - const { activePools } = useActivePools({ - who: activeAccount, - }); - - // Calculate the number of _other_ pools the user has a role in. - const poolRoleCount = Object.keys(activePools).filter( - (poolId) => poolId !== String(membership?.poolId) - ).length; const ROW_HEIGHT = 220; @@ -81,18 +62,6 @@ export const HomeInner = () => { badge: String(favorites.length), }, ]} - button={ - !poolMembersipSyncing() && poolRoleCount > 0 - ? { - title: t('pools.allRoles'), - onClick: () => - openModal({ - key: 'AccountPoolRoles', - options: { who: activeAccount, activePools }, - }), - } - : undefined - } /> {activeTab === 0 && ( <> diff --git a/packages/app/src/types.ts b/packages/app/src/types.ts index 416b92bed7..edd705037e 100644 --- a/packages/app/src/types.ts +++ b/packages/app/src/types.ts @@ -11,14 +11,19 @@ import type { APIStakingMetrics, } from 'contexts/Api/types'; import type { ActiveBalance } from 'contexts/Balances/types'; +import type { BondedAccount } from 'contexts/Bonded/types'; +import type { FastUnstakeQueueResult } from 'contexts/FastUnstake/types'; import type { Theme } from 'contexts/Themes/types'; import type { DetailActivePool } from 'controllers/ActivePools/types'; import type { NotificationItem } from 'controllers/Notifications/types'; import type { OnlineStatusEvent } from 'controllers/OnlineStatus/types'; import type { PayoutType } from 'controllers/Subscan/types'; import type { SyncEvent } from 'controllers/Sync/types'; -import type { APIEventDetail } from 'model/Api/types'; +import type { APIEventDetail, PapiReadyEvent } from 'model/Api/types'; +import type { AccountProxiesEvent } from 'model/Subscribe/AccountProxies/types'; import type { BlockNumberEventDetail } from 'model/Subscribe/BlockNumber/types'; +import type { FastUnstakeConfigResult } from 'model/Subscribe/FastUnstakeConfig/types'; +import type { PoolMemberBatchEvent } from 'model/Subscribe/PoolMembersMulti/types'; import type { FC, FunctionComponent, SVGProps } from 'react'; declare global { @@ -30,6 +35,7 @@ declare global { interface DocumentEventMap { notification: CustomEvent; 'api-status': CustomEvent; + 'papi-ready': CustomEvent; 'online-status': CustomEvent; 'new-block-number': CustomEvent; 'new-network-metrics': CustomEvent<{ @@ -41,6 +47,11 @@ declare global { stakingMetrics: APIStakingMetrics; }>; 'new-active-pool': CustomEvent; + 'new-pool-members-batch': CustomEvent; + 'new-fast-unstake-config': CustomEvent; + 'new-fast-unstake-deposit': CustomEvent; + 'new-account-proxies': CustomEvent; + 'new-bonded-account': CustomEvent; 'new-sync-status': CustomEvent; 'new-external-account': CustomEvent<{ address: string }>; 'new-account-balance': CustomEvent; @@ -66,7 +77,8 @@ type NetworkColor = export interface Network { name: NetworkName; endpoints: { - lightClient: string; + lightClientKey: string; + lightClient: () => Promise; defaultRpcEndpoint: string; rpcEndpoints: Record; }; @@ -102,9 +114,11 @@ export interface SystemChain { units: number; unit: string; endpoints: { - lightClient: string; + lightClientKey: string; + lightClient: () => Promise; rpcEndpoints: Record; }; + relayChain: NetworkName; } export interface PageCategory { diff --git a/packages/consts/src/index.ts b/packages/consts/src/index.ts index 82fd0d2582..92c9d1cd8a 100644 --- a/packages/consts/src/index.ts +++ b/packages/consts/src/index.ts @@ -1,8 +1,6 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import { stringToU8a } from '@polkadot/util'; - /* * Global Constants */ @@ -11,13 +9,6 @@ export const ManualSigners = ['ledger', 'vault', 'wallet_connect']; export const DiscordSupportUrl = 'https://discord.gg/QY7CSSJm3D'; export const MailSupportAddress = 'staking@polkadot.cloud'; -/* - * Byte Helpers - */ -export const EmptyH256 = new Uint8Array(32); -export const ModPrefix = stringToU8a('modl'); -export const U32Opts = { bitLength: 32, isLe: true }; - /* * Element Thresholds */ diff --git a/packages/locales/src/resources/cn/pages.json b/packages/locales/src/resources/cn/pages.json index 6e0901e6b8..569edeb874 100644 --- a/packages/locales/src/resources/cn/pages.json +++ b/packages/locales/src/resources/cn/pages.json @@ -138,7 +138,6 @@ "addressCopied": "地址已复制到剪贴板", "addressInvalid": "地址无效", "allPools": "所有提名池", - "allRoles": "所有角色", "assigned": "己分配", "assignedToAnyAccount": " 您的主理人提名人守护人角色可以分配给任何帐户。", "availableToClaim": "成员可申领的奖励{{unit}}金额", diff --git a/packages/locales/src/resources/en/pages.json b/packages/locales/src/resources/en/pages.json index dd80be38d7..6501757479 100644 --- a/packages/locales/src/resources/en/pages.json +++ b/packages/locales/src/resources/en/pages.json @@ -140,7 +140,6 @@ "addressCopied": "Address Copied to Clipboard", "addressInvalid": "Address Invalid", "allPools": "All Pools", - "allRoles": "All Roles", "assigned": "Assigned", "assignedToAnyAccount": " Your Root, Nominator and Bouncer roles can be assigned to any account.", "availableToClaim": "The outstanding amount of {{unit}} available to claim by pool members.", diff --git a/yarn.lock b/yarn.lock index a47ab23406..f57a63be72 100644 --- a/yarn.lock +++ b/yarn.lock @@ -59,7 +59,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.25.9, @babel/code-frame@npm:^7.26.0": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.25.9, @babel/code-frame@npm:^7.26.0": version: 7.26.2 resolution: "@babel/code-frame@npm:7.26.2" dependencies: @@ -236,17 +236,6 @@ __metadata: languageName: node linkType: hard -"@chainsafe/metamask-polkadot-adapter@npm:^0.6.0": - version: 0.6.0 - resolution: "@chainsafe/metamask-polkadot-adapter@npm:0.6.0" - dependencies: - "@polkadot/api": "npm:^10.9.1" - "@polkadot/extension-inject": "npm:^0.46.5" - "@polkadot/types-augment": "npm:^10.9.1" - checksum: 10c0/a9cdfdd175dc6d9f6aa1d6f4515185d3f2b4089154dd0cd47749f66c6e5965cd7ff0187245763f7aafa1be49d41e1d2e8d721c78a0e831afc506fb6ff229ae15 - languageName: node - linkType: hard - "@coinbase/wallet-sdk@npm:4.2.3": version: 4.2.3 resolution: "@coinbase/wallet-sdk@npm:4.2.3" @@ -259,6 +248,15 @@ __metadata: languageName: node linkType: hard +"@commander-js/extra-typings@npm:^12.1.0": + version: 12.1.0 + resolution: "@commander-js/extra-typings@npm:12.1.0" + peerDependencies: + commander: ~12.1.0 + checksum: 10c0/5d29eaa724b577e2a52a393ad54992924d2559931b8e493ab892477b7a4e878e475c6bf771260f8585d835f7d8e17ae4a2656c191e9595d210ae0b48291c0b3d + languageName: node + linkType: hard + "@dotlottie/common@npm:0.7.11": version: 0.7.11 resolution: "@dotlottie/common@npm:0.7.11" @@ -343,6 +341,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/aix-ppc64@npm:0.24.0" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/android-arm64@npm:0.21.5" @@ -350,6 +355,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/android-arm64@npm:0.24.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/android-arm@npm:0.21.5" @@ -357,6 +369,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/android-arm@npm:0.24.0" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/android-x64@npm:0.21.5" @@ -364,6 +383,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/android-x64@npm:0.24.0" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/darwin-arm64@npm:0.21.5" @@ -371,6 +397,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/darwin-arm64@npm:0.24.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/darwin-x64@npm:0.21.5" @@ -378,6 +411,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/darwin-x64@npm:0.24.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/freebsd-arm64@npm:0.21.5" @@ -385,6 +425,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/freebsd-arm64@npm:0.24.0" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/freebsd-x64@npm:0.21.5" @@ -392,6 +439,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/freebsd-x64@npm:0.24.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-arm64@npm:0.21.5" @@ -399,6 +453,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/linux-arm64@npm:0.24.0" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-arm@npm:0.21.5" @@ -406,6 +467,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/linux-arm@npm:0.24.0" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-ia32@npm:0.21.5" @@ -413,6 +481,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/linux-ia32@npm:0.24.0" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-loong64@npm:0.21.5" @@ -420,6 +495,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/linux-loong64@npm:0.24.0" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-mips64el@npm:0.21.5" @@ -427,6 +509,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/linux-mips64el@npm:0.24.0" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-ppc64@npm:0.21.5" @@ -434,6 +523,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/linux-ppc64@npm:0.24.0" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-riscv64@npm:0.21.5" @@ -441,6 +537,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/linux-riscv64@npm:0.24.0" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-s390x@npm:0.21.5" @@ -448,6 +551,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/linux-s390x@npm:0.24.0" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-x64@npm:0.21.5" @@ -455,6 +565,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/linux-x64@npm:0.24.0" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/netbsd-x64@npm:0.21.5" @@ -462,6 +579,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/netbsd-x64@npm:0.24.0" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/openbsd-arm64@npm:0.24.0" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/openbsd-x64@npm:0.21.5" @@ -469,6 +600,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/openbsd-x64@npm:0.24.0" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/sunos-x64@npm:0.21.5" @@ -476,6 +614,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/sunos-x64@npm:0.24.0" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/win32-arm64@npm:0.21.5" @@ -483,6 +628,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/win32-arm64@npm:0.24.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/win32-ia32@npm:0.21.5" @@ -490,6 +642,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/win32-ia32@npm:0.24.0" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/win32-x64@npm:0.21.5" @@ -497,6 +656,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/win32-x64@npm:0.24.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": version: 4.4.1 resolution: "@eslint-community/eslint-utils@npm:4.4.1" @@ -615,46 +781,46 @@ __metadata: languageName: node linkType: hard -"@fortawesome/fontawesome-common-types@npm:6.6.0": - version: 6.6.0 - resolution: "@fortawesome/fontawesome-common-types@npm:6.6.0" - checksum: 10c0/f76e5959f6ce01355f599126a3a68facba578dc8ebb7ad40fbd22417b7056364a577c1887720ec9653d4efa5b704a01150f5064fc7de237d697fd80e3d9c83aa +"@fortawesome/fontawesome-common-types@npm:6.7.1": + version: 6.7.1 + resolution: "@fortawesome/fontawesome-common-types@npm:6.7.1" + checksum: 10c0/0345b896ad8df26fb823893ce0a405dd513e9ef34878dd7b94fbbbabe370379bf680a32199bcaaf7346acbcd2a10bf0072e305829b49e5d9b0bf1c5315d07c96 languageName: node linkType: hard "@fortawesome/fontawesome-svg-core@npm:^6.5.2": - version: 6.6.0 - resolution: "@fortawesome/fontawesome-svg-core@npm:6.6.0" + version: 6.7.1 + resolution: "@fortawesome/fontawesome-svg-core@npm:6.7.1" dependencies: - "@fortawesome/fontawesome-common-types": "npm:6.6.0" - checksum: 10c0/38e2840791711524a3c57d9ea48a5a2e99da6fa3c657ba6beaad7ec3b8da31489a9e38f42b23d70584c75b579dc1ff8c67e075bc9789032278e4da54bb86ecfe + "@fortawesome/fontawesome-common-types": "npm:6.7.1" + checksum: 10c0/7a1dc40fc5ef380eb0f83d89dfcb26dc5b49f6a6545970e46e413d81dfa9764006a056cfcd49c2e8113dbd2c95d429db873710424a150e4a923b2496e11707ca languageName: node linkType: hard "@fortawesome/free-brands-svg-icons@npm:^6.5.2": - version: 6.6.0 - resolution: "@fortawesome/free-brands-svg-icons@npm:6.6.0" + version: 6.7.1 + resolution: "@fortawesome/free-brands-svg-icons@npm:6.7.1" dependencies: - "@fortawesome/fontawesome-common-types": "npm:6.6.0" - checksum: 10c0/1135a22ff274939da477496f550b6750a1b5fd0ddd0c09bddb1874f2c183a5c8edb519de2cebf6454b12a8457c3eec587bdb6f68e96140cceeb6d02c1ec35479 + "@fortawesome/fontawesome-common-types": "npm:6.7.1" + checksum: 10c0/bd140ed872582300f7ed4b2af9348791cae38217ee0685ea34c135b00ce8db07da17b518464162a29d371f2f7177d3051b02535ca6b560bb15981ef34ef8e112 languageName: node linkType: hard "@fortawesome/free-regular-svg-icons@npm:^6.5.2": - version: 6.6.0 - resolution: "@fortawesome/free-regular-svg-icons@npm:6.6.0" + version: 6.7.1 + resolution: "@fortawesome/free-regular-svg-icons@npm:6.7.1" dependencies: - "@fortawesome/fontawesome-common-types": "npm:6.6.0" - checksum: 10c0/c682a6d7c6bdce492eee5b15a6647f9c436ce04f337080b7061cc04a739b5eb95224f7cdc7d865cf08fea837d4d1b1541849a3183534956e176896a969220d45 + "@fortawesome/fontawesome-common-types": "npm:6.7.1" + checksum: 10c0/6d770622cd6519835d8557a0a562c5f2f877527bb65671a6136a558ce61024bf7baaeb8e29420f29da4893b815c3af8173c6f45064bbcc040db613998d77502e languageName: node linkType: hard "@fortawesome/free-solid-svg-icons@npm:^6.5.2": - version: 6.6.0 - resolution: "@fortawesome/free-solid-svg-icons@npm:6.6.0" + version: 6.7.1 + resolution: "@fortawesome/free-solid-svg-icons@npm:6.7.1" dependencies: - "@fortawesome/fontawesome-common-types": "npm:6.6.0" - checksum: 10c0/34828d5e682c6f9d19e3a892ff8a390128fa7dc68768b11c727c11b6a05e5efc929206bfbec83e9d3ae0590a6f6ea22fd5e447fea647e560650f7f3ef1cff543 + "@fortawesome/fontawesome-common-types": "npm:6.7.1" + checksum: 10c0/9b6e6ba383dfc456020b77a600edcccaf8131ebd472038bd6b6f2425f011c8c63c0a6049d798dc120512b9c5332043eb46bb815d9d55c56ebdf8ecc94afb0184 languageName: node linkType: hard @@ -915,7 +1081,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/gen-mapping@npm:^0.3.5": +"@jridgewell/gen-mapping@npm:^0.3.2, @jridgewell/gen-mapping@npm:^0.3.5": version: 0.3.5 resolution: "@jridgewell/gen-mapping@npm:0.3.5" dependencies: @@ -958,9 +1124,9 @@ __metadata: linkType: hard "@kurkle/color@npm:^0.3.0": - version: 0.3.2 - resolution: "@kurkle/color@npm:0.3.2" - checksum: 10c0/a9e8e3e35dcd59dec4dd4f0105919c05e24823a96347bcf152965c29e48d6290b66d5fb96c071875db752e10930724c48ce6d338fefbd65e0ce5082d5c78970e + version: 0.3.4 + resolution: "@kurkle/color@npm:0.3.4" + checksum: 10c0/0e9fd55c614b005c5f0c4c755bca19ec0293bc7513b4ea3ec1725234f9c2fa81afbc78156baf555c8b9cb0d305619253c3f5bca016067daeebb3d00ebb4ea683 languageName: node linkType: hard @@ -1378,9 +1544,9 @@ __metadata: linkType: hard "@noble/ciphers@npm:^1.0.0": - version: 1.0.0 - resolution: "@noble/ciphers@npm:1.0.0" - checksum: 10c0/6c04d6e9d10a922fff170efc44622c95a25fb817f4b593e0f150dd27599576f3fe3c5b61eb02054b22d1507e3839879ddd5acb2d2acf8efbea4efab99bbcd333 + version: 1.1.0 + resolution: "@noble/ciphers@npm:1.1.0" + checksum: 10c0/18313ccdeb8337d8739e3178cc9c1c30866453b9f398d0b195b7d4b336af8ebf334da2c2c7afa83f2bcd2c57243e2686354d41bb72090e586131f421518b5be1 languageName: node linkType: hard @@ -1393,7 +1559,7 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:1.6.0, @noble/curves@npm:^1.3.0, @noble/curves@npm:^1.4.0, @noble/curves@npm:^1.6.0, @noble/curves@npm:~1.6.0": +"@noble/curves@npm:1.6.0, @noble/curves@npm:~1.6.0": version: 1.6.0 resolution: "@noble/curves@npm:1.6.0" dependencies: @@ -1402,6 +1568,15 @@ __metadata: languageName: node linkType: hard +"@noble/curves@npm:^1.3.0, @noble/curves@npm:^1.4.0, @noble/curves@npm:^1.6.0, @noble/curves@npm:~1.7.0": + version: 1.7.0 + resolution: "@noble/curves@npm:1.7.0" + dependencies: + "@noble/hashes": "npm:1.6.0" + checksum: 10c0/3317ec9b7699d2476707a89ceb3ddce60e69bac287561a31dd533669408633e093860fea5067eb9c54e5a7ced0705da1cba8859b6b1e0c48d3afff55fe2e77d0 + languageName: node + linkType: hard + "@noble/hashes@npm:1.4.0, @noble/hashes@npm:~1.4.0": version: 1.4.0 resolution: "@noble/hashes@npm:1.4.0" @@ -1409,13 +1584,27 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.5.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.3.3, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:^1.5.0, @noble/hashes@npm:~1.5.0": +"@noble/hashes@npm:1.5.0, @noble/hashes@npm:~1.5.0": version: 1.5.0 resolution: "@noble/hashes@npm:1.5.0" checksum: 10c0/1b46539695fbfe4477c0822d90c881a04d4fa2921c08c552375b444a48cac9930cb1ee68de0a3c7859e676554d0f3771999716606dc4d8f826e414c11692cdd9 languageName: node linkType: hard +"@noble/hashes@npm:1.6.0": + version: 1.6.0 + resolution: "@noble/hashes@npm:1.6.0" + checksum: 10c0/e7e75898257fb36d933935fcdf1cc67ca7c083eb7b2411aa57fde7eb494c2cea0bec03686462032e25d5b0e1e4ab7357d1afb6718f6a68515db1f392141e9f14 + languageName: node + linkType: hard + +"@noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.3.3, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:^1.5.0, @noble/hashes@npm:~1.6.0": + version: 1.6.1 + resolution: "@noble/hashes@npm:1.6.1" + checksum: 10c0/27643cd8b551bc933b57cc29aa8c8763d586552fc4c3e06ecf7897f55be3463c0c9dff7f6ebacd88e5ce6d0cdb5415ca4874d0cf4359b5ea4a85be21ada03aab + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -1641,24 +1830,72 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/client@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": - version: 0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0 - resolution: "@polkadot-api/client@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" +"@polkadot-api/cli@npm:0.9.21": + version: 0.9.21 + resolution: "@polkadot-api/cli@npm:0.9.21" dependencies: - "@polkadot-api/metadata-builders": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - "@polkadot-api/substrate-bindings": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - "@polkadot-api/substrate-client": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - "@polkadot-api/utils": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - peerDependencies: - rxjs: ">=7.8.0" - checksum: 10c0/f80744e1612446a09fdd3f5a98cdf8f9a5b9fdeddcfa083084b49e0ae63e4b4875a326487258bcbb60e6edc2fbb831436abe4a48b87942c4417ba1ee05e95648 + "@commander-js/extra-typings": "npm:^12.1.0" + "@polkadot-api/codegen": "npm:0.12.9" + "@polkadot-api/ink-contracts": "npm:0.2.2" + "@polkadot-api/json-rpc-provider": "npm:0.0.4" + "@polkadot-api/known-chains": "npm:0.5.8" + "@polkadot-api/metadata-compatibility": "npm:0.1.12" + "@polkadot-api/observable-client": "npm:0.6.3" + "@polkadot-api/polkadot-sdk-compat": "npm:2.3.1" + "@polkadot-api/sm-provider": "npm:0.1.7" + "@polkadot-api/smoldot": "npm:0.3.7" + "@polkadot-api/substrate-bindings": "npm:0.9.4" + "@polkadot-api/substrate-client": "npm:0.3.0" + "@polkadot-api/utils": "npm:0.1.2" + "@polkadot-api/wasm-executor": "npm:^0.1.2" + "@polkadot-api/ws-provider": "npm:0.3.6" + "@types/node": "npm:^22.9.1" + commander: "npm:^12.1.0" + execa: "npm:^9.5.1" + fs.promises.exists: "npm:^1.1.4" + ora: "npm:^8.1.1" + read-pkg: "npm:^9.0.1" + rxjs: "npm:^7.8.1" + tsc-prog: "npm:^2.3.0" + tsup: "npm:^8.3.5" + typescript: "npm:^5.6.3" + write-package: "npm:^7.1.0" + bin: + papi: dist/main.js + polkadot-api: dist/main.js + checksum: 10c0/0d0e26e84998c6ddf5a13e1f701af5204c35ab06b7cdc3f8b1221cedd361922a5db904fbb32f0bb1416dd3f6570a1d36a6d7c94101612605a93baea77f32794a + languageName: node + linkType: hard + +"@polkadot-api/codegen@npm:0.12.9": + version: 0.12.9 + resolution: "@polkadot-api/codegen@npm:0.12.9" + dependencies: + "@polkadot-api/ink-contracts": "npm:0.2.2" + "@polkadot-api/metadata-builders": "npm:0.9.2" + "@polkadot-api/metadata-compatibility": "npm:0.1.12" + "@polkadot-api/substrate-bindings": "npm:0.9.4" + "@polkadot-api/utils": "npm:0.1.2" + checksum: 10c0/9dce88598104cbe7f237860bbc31485e9ab84eac761b9a8ab0521f075b34915233ea855cf9245ca1c013e933ef8fa7aaecac376513b7e1c829f7b62f035c963e + languageName: node + linkType: hard + +"@polkadot-api/ink-contracts@npm:0.2.2": + version: 0.2.2 + resolution: "@polkadot-api/ink-contracts@npm:0.2.2" + dependencies: + "@polkadot-api/metadata-builders": "npm:0.9.2" + "@polkadot-api/substrate-bindings": "npm:0.9.4" + "@polkadot-api/utils": "npm:0.1.2" + scale-ts: "npm:^1.6.1" + checksum: 10c0/86ab565f94205f47887917b2193c5825c0f27a027442a2240a64e2e6885743334b4c526fb896cb6ad411311ae4c9a122fc37e98434b41aa895300e633d6347fb languageName: node linkType: hard -"@polkadot-api/json-rpc-provider-proxy@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": - version: 0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0 - resolution: "@polkadot-api/json-rpc-provider-proxy@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - checksum: 10c0/63160c2227996e1d8926a7d0281cd7a747e53e8049a63c74087425bca6a986662f46ca8e0be5b31882816199aeef1863c8ee9a7d2834d2ee1a0d1bc6dc7d61f3 +"@polkadot-api/json-rpc-provider-proxy@npm:0.2.4": + version: 0.2.4 + resolution: "@polkadot-api/json-rpc-provider-proxy@npm:0.2.4" + checksum: 10c0/bf99d5a27e02dc5b3641d4c17ec28d7ac72b7ad077e99a311f3c2709000957429321ae93926c941024539c521a118d45251fc8ab3f8fc11334e1385a2f8908ff languageName: node linkType: hard @@ -1676,31 +1913,37 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/json-rpc-provider@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": - version: 0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0 - resolution: "@polkadot-api/json-rpc-provider@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - checksum: 10c0/2cc039dfd0234696e624a48d94f07b68ab3b1fc402f703b142b05f640301c47a3c5315a6ef10d2a61c31d6f93181d1240ba5b4e169e5cb8a428f5eb9613b0123 +"@polkadot-api/json-rpc-provider@npm:0.0.4": + version: 0.0.4 + resolution: "@polkadot-api/json-rpc-provider@npm:0.0.4" + checksum: 10c0/5615394380bff2d8885592c8001055f1b006e3dd2f650b6a0e41ec836d61e903d78cc4ed06297827020523b0eecc9dff0af661fed7ae82523c97e294a2dc2e27 languageName: node linkType: hard -"@polkadot-api/merkleize-metadata@npm:^1.1.4": - version: 1.1.9 - resolution: "@polkadot-api/merkleize-metadata@npm:1.1.9" +"@polkadot-api/known-chains@npm:0.5.8": + version: 0.5.8 + resolution: "@polkadot-api/known-chains@npm:0.5.8" + checksum: 10c0/2f0dfd5f610927f00f41245113952c581cfa2b3fc0e1e29578ac52ca87b44a24aad5d10b76e3a79022cd6da46bb02048c854537004aee6da3df1a61bd53e0c72 + languageName: node + linkType: hard + +"@polkadot-api/logs-provider@npm:0.0.6": + version: 0.0.6 + resolution: "@polkadot-api/logs-provider@npm:0.0.6" dependencies: - "@polkadot-api/metadata-builders": "npm:0.9.1" - "@polkadot-api/substrate-bindings": "npm:0.9.3" - "@polkadot-api/utils": "npm:0.1.2" - checksum: 10c0/096ec12790095e6f11e89585caaa061195bcf4724bf8ddec478cf03513c3dc667e8d28ee0aebdbb715674b9763941432d59ab6cbbec4f66a1b12bc8ee3b16f8a + "@polkadot-api/json-rpc-provider": "npm:0.0.4" + checksum: 10c0/0c7d22b7a9cc495539f3f4a61d0d77882139bcfbb8159d8ffadbc7f2b6a8dd093f217fd2266df60417bc7b23582d2c3659052e17bb9b1be664abf76edf7ce558 languageName: node linkType: hard -"@polkadot-api/metadata-builders@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": - version: 0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0 - resolution: "@polkadot-api/metadata-builders@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" +"@polkadot-api/merkleize-metadata@npm:^1.1.4": + version: 1.1.10 + resolution: "@polkadot-api/merkleize-metadata@npm:1.1.10" dependencies: - "@polkadot-api/substrate-bindings": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - "@polkadot-api/utils": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - checksum: 10c0/15dcba05a0c7cafaab72edc6c6246133cc25f94a158f0ac2091e3c774271cae6cf64658cefe5db9d57eb16e9a9cc8936f387547064237d129d7675e9ca27e1d3 + "@polkadot-api/metadata-builders": "npm:0.9.2" + "@polkadot-api/substrate-bindings": "npm:0.9.4" + "@polkadot-api/utils": "npm:0.1.2" + checksum: 10c0/77f4e8cd7537228806e5d4d01f7bd385d3d46b633ab84ca8a7a8e0293a4b86487175081a6d0dd204f0ca9050dfb713e1354546d4f30c764b44875a9e1082a34d languageName: node linkType: hard @@ -1714,13 +1957,37 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/metadata-builders@npm:0.9.1": - version: 0.9.1 - resolution: "@polkadot-api/metadata-builders@npm:0.9.1" +"@polkadot-api/metadata-builders@npm:0.9.2": + version: 0.9.2 + resolution: "@polkadot-api/metadata-builders@npm:0.9.2" + dependencies: + "@polkadot-api/substrate-bindings": "npm:0.9.4" + "@polkadot-api/utils": "npm:0.1.2" + checksum: 10c0/2a5171e6659f0d99d247d4483ce7832bd803f563ce5eac59dbecd40d84eee4f0ff31ccbfdcd9c85fd805b5aeb4d865975f87ceba6dbb0de04753144988e8074f + languageName: node + linkType: hard + +"@polkadot-api/metadata-compatibility@npm:0.1.12": + version: 0.1.12 + resolution: "@polkadot-api/metadata-compatibility@npm:0.1.12" + dependencies: + "@polkadot-api/metadata-builders": "npm:0.9.2" + "@polkadot-api/substrate-bindings": "npm:0.9.4" + checksum: 10c0/b3aa63d3c9590cb4eff58825b5276bf523715f63277c5e2b048c2c5f7af8855fc2507b6a3142dddc8bc55188c8e423e707e35dfb2145154c84af78e0e1b0f4bb + languageName: node + linkType: hard + +"@polkadot-api/observable-client@npm:0.6.3": + version: 0.6.3 + resolution: "@polkadot-api/observable-client@npm:0.6.3" dependencies: - "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/metadata-builders": "npm:0.9.2" + "@polkadot-api/substrate-bindings": "npm:0.9.4" "@polkadot-api/utils": "npm:0.1.2" - checksum: 10c0/768c14b578fe2d25c58904479a6d37da38f86e84fe11e01d16a86d56640138a2efb67c0706578e871de8529106491b9299173422d29259b045387cf7a35c6210 + peerDependencies: + "@polkadot-api/substrate-client": 0.3.0 + rxjs: ">=7.8.0" + checksum: 10c0/e5cc27b07546a78f892495c87c89e232458d0548311f3a5b55e0b2fe1745e2576575455f4af4cc0b13869d977e440f30e66476faf23dafe79996952a90320e3b languageName: node linkType: hard @@ -1738,15 +2005,79 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/substrate-bindings@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": - version: 0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0 - resolution: "@polkadot-api/substrate-bindings@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" +"@polkadot-api/pjs-signer@npm:0.6.1": + version: 0.6.1 + resolution: "@polkadot-api/pjs-signer@npm:0.6.1" dependencies: - "@noble/hashes": "npm:^1.3.1" - "@polkadot-api/utils": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - "@scure/base": "npm:^1.1.1" - scale-ts: "npm:^1.6.0" - checksum: 10c0/f6273cbb1ed74f253f83dfb9e4b46375eb7430e17e69a2f3d631ff80d8ff7b7e871cdf89639ef18635a659a60623e384c894ab2e1889d70442f7ca73c05b44e3 + "@polkadot-api/metadata-builders": "npm:0.9.2" + "@polkadot-api/polkadot-signer": "npm:0.1.6" + "@polkadot-api/signers-common": "npm:0.1.2" + "@polkadot-api/substrate-bindings": "npm:0.9.4" + "@polkadot-api/utils": "npm:0.1.2" + checksum: 10c0/76ec7481d36a575788303ec998d7c41246728ad82af4229ff33f8dd889ca8a8f109dd043ed69d38b7d31398355e8afe8864760287ab6e3de96718c3427654127 + languageName: node + linkType: hard + +"@polkadot-api/polkadot-sdk-compat@npm:2.3.1": + version: 2.3.1 + resolution: "@polkadot-api/polkadot-sdk-compat@npm:2.3.1" + dependencies: + "@polkadot-api/json-rpc-provider": "npm:0.0.4" + checksum: 10c0/19f55588fbac5b72a8e0d54e7c5958b3de76907217bba94118aa409e86b1dc1a86f392da724a258a409678fd00ae21c07d0ade566a6238a84abd10cbed6952c9 + languageName: node + linkType: hard + +"@polkadot-api/polkadot-signer@npm:0.1.6": + version: 0.1.6 + resolution: "@polkadot-api/polkadot-signer@npm:0.1.6" + checksum: 10c0/26ab65ac02cfc9dcc8b9539f44f0e72f8faa8dbbb7cc68c5922e2ebdf71bcca0641e70f1fb0fec46de4f98f36448164c7025ef68bc21fa635b8bbb15f5731f10 + languageName: node + linkType: hard + +"@polkadot-api/signer@npm:0.1.11": + version: 0.1.11 + resolution: "@polkadot-api/signer@npm:0.1.11" + dependencies: + "@noble/hashes": "npm:^1.5.0" + "@polkadot-api/polkadot-signer": "npm:0.1.6" + "@polkadot-api/signers-common": "npm:0.1.2" + "@polkadot-api/substrate-bindings": "npm:0.9.4" + "@polkadot-api/utils": "npm:0.1.2" + checksum: 10c0/35048942c7dce5cec4918fd19fb6c4e8797b4a5a522ec4757f7f0db5eb9fec0e534269327dc09efb092e08c0a81f96b84133ec7a1125eeffbd1a48fd8d1cad71 + languageName: node + linkType: hard + +"@polkadot-api/signers-common@npm:0.1.2, @polkadot-api/signers-common@npm:^0.1.1": + version: 0.1.2 + resolution: "@polkadot-api/signers-common@npm:0.1.2" + dependencies: + "@polkadot-api/metadata-builders": "npm:0.9.2" + "@polkadot-api/polkadot-signer": "npm:0.1.6" + "@polkadot-api/substrate-bindings": "npm:0.9.4" + "@polkadot-api/utils": "npm:0.1.2" + checksum: 10c0/5bbb36f0258ec37e9daf459d40252329cb721a83dd1b1867a9f90e44946ab69b2f6ec8f11cb30ff330f4222280b1a92f828648e1b4edee341779d7849e909140 + languageName: node + linkType: hard + +"@polkadot-api/sm-provider@npm:0.1.7": + version: 0.1.7 + resolution: "@polkadot-api/sm-provider@npm:0.1.7" + dependencies: + "@polkadot-api/json-rpc-provider": "npm:0.0.4" + "@polkadot-api/json-rpc-provider-proxy": "npm:0.2.4" + peerDependencies: + "@polkadot-api/smoldot": ">=0.3" + checksum: 10c0/91a1b8cc054f5b8f5ec25a40d9bc876ad5e9138a9801b22a2e4f6bf396ed584956cff0de7349d9b435fe5c04bad9137e59edcb6960943b9126e7262c9a0d8740 + languageName: node + linkType: hard + +"@polkadot-api/smoldot@npm:0.3.7": + version: 0.3.7 + resolution: "@polkadot-api/smoldot@npm:0.3.7" + dependencies: + "@types/node": "npm:^22.9.0" + smoldot: "npm:2.0.33" + checksum: 10c0/edd2e4b31a3f43231e86dc9452c8cf9c126858c48539d35d5f01146a6958db8f530dc01a9aaf01ffb8b7f0b3750cdc16223df80d548e212955de889defced114 languageName: node linkType: hard @@ -1762,22 +2093,25 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/substrate-bindings@npm:0.9.3, @polkadot-api/substrate-bindings@npm:^0.9.3": - version: 0.9.3 - resolution: "@polkadot-api/substrate-bindings@npm:0.9.3" +"@polkadot-api/substrate-bindings@npm:0.9.4, @polkadot-api/substrate-bindings@npm:^0.9.3": + version: 0.9.4 + resolution: "@polkadot-api/substrate-bindings@npm:0.9.4" dependencies: - "@noble/hashes": "npm:^1.4.0" + "@noble/hashes": "npm:^1.5.0" "@polkadot-api/utils": "npm:0.1.2" - "@scure/base": "npm:^1.1.7" + "@scure/base": "npm:^1.1.9" scale-ts: "npm:^1.6.1" - checksum: 10c0/d0b2e9adf3b28e7aaa1f52e26fd7b200defe1b83499450227289bff0ec784e7dab334e93dc8d989e10e31d6cbb3ac561afd50f1c25e54b9ddc2be02826ca43c2 + checksum: 10c0/eb4226807a07ed8f65ae641a1973d8b0b97410392b7c8d23002c9881d4098f31e31a14157a4e4adf250afd66355406a6108995d14a72ff3dfc15b17f314d2e61 languageName: node linkType: hard -"@polkadot-api/substrate-client@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": - version: 0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0 - resolution: "@polkadot-api/substrate-client@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - checksum: 10c0/56dc28993d06a14b4091f028956f8bb179d3c602a3e3979a978eb2192233ce6ad2f12d4a583e859480521d4c75629aab1d7f0effd989da0ebb90a1b51e7ee073 +"@polkadot-api/substrate-client@npm:0.3.0": + version: 0.3.0 + resolution: "@polkadot-api/substrate-client@npm:0.3.0" + dependencies: + "@polkadot-api/json-rpc-provider": "npm:0.0.4" + "@polkadot-api/utils": "npm:0.1.2" + checksum: 10c0/a1eba558065f596f2fecf40d4bcef4e3ea50344b76bf5e890bbb17e4ff0b1fb251cf484bf2b8ef49ebcce5c2fc84ee1617e0769d2d6f740faf0caa623d8a6a12 languageName: node linkType: hard @@ -1791,13 +2125,6 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/utils@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": - version: 0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0 - resolution: "@polkadot-api/utils@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - checksum: 10c0/63c36ac45d63a4f8718d1324be0290dadfbfbb387e440d5962de13200861400a6af09a5e96b73f985fd4c27d03c93927420c8145e49e1e822653a3c223dda645 - languageName: node - linkType: hard - "@polkadot-api/utils@npm:0.1.0": version: 0.1.0 resolution: "@polkadot-api/utils@npm:0.1.0" @@ -1812,155 +2139,102 @@ __metadata: languageName: node linkType: hard -"@polkadot/api-augment@npm:10.13.1": - version: 10.13.1 - resolution: "@polkadot/api-augment@npm:10.13.1" - dependencies: - "@polkadot/api-base": "npm:10.13.1" - "@polkadot/rpc-augment": "npm:10.13.1" - "@polkadot/types": "npm:10.13.1" - "@polkadot/types-augment": "npm:10.13.1" - "@polkadot/types-codec": "npm:10.13.1" - "@polkadot/util": "npm:^12.6.2" - tslib: "npm:^2.6.2" - checksum: 10c0/5f1faa67bc8a574fe97debc8aa7e7f6795aa437a1212121188d6bbeb1d465b47b0a1a22b8268f3136e1956dcdf0c1f9004b2a7968a275414e93b0a4e91ba7edc - languageName: node - linkType: hard - -"@polkadot/api-augment@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/api-augment@npm:14.3.1" - dependencies: - "@polkadot/api-base": "npm:14.3.1" - "@polkadot/rpc-augment": "npm:14.3.1" - "@polkadot/types": "npm:14.3.1" - "@polkadot/types-augment": "npm:14.3.1" - "@polkadot/types-codec": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - tslib: "npm:^2.8.0" - checksum: 10c0/2cb756c6b2d2e9249ef8706725ee83644e2bde323a916ade1976a653521c2680bcffef25455aeb58627ac48492b99abb89a0aeac75cfa26837fda5dde4011548 - languageName: node - linkType: hard - -"@polkadot/api-base@npm:10.13.1": - version: 10.13.1 - resolution: "@polkadot/api-base@npm:10.13.1" - dependencies: - "@polkadot/rpc-core": "npm:10.13.1" - "@polkadot/types": "npm:10.13.1" - "@polkadot/util": "npm:^12.6.2" - rxjs: "npm:^7.8.1" - tslib: "npm:^2.6.2" - checksum: 10c0/3adaa5d3c34e16cc28a0427839c87aab7b1d022c8b6992c43dc91ab7e910d0c8e17dca9ee6b7c9e27a6486aa8878dd7deae79870d7d44ede92aba8421241c249 +"@polkadot-api/wasm-executor@npm:^0.1.2": + version: 0.1.2 + resolution: "@polkadot-api/wasm-executor@npm:0.1.2" + checksum: 10c0/0577111cf7ee12e1606ba863385f50670f2d073e9032efd8365e578f03f4a8b01e699641e19b2aec15e36832aa3b6b0724951fa84d23e87492d0532f55e291bf languageName: node linkType: hard -"@polkadot/api-base@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/api-base@npm:14.3.1" +"@polkadot-api/ws-provider@npm:0.3.6": + version: 0.3.6 + resolution: "@polkadot-api/ws-provider@npm:0.3.6" dependencies: - "@polkadot/rpc-core": "npm:14.3.1" - "@polkadot/types": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - rxjs: "npm:^7.8.1" - tslib: "npm:^2.8.0" - checksum: 10c0/8e04af99ce4594fbbf934706ff3b77a54da9c13feaa5563c50de4add2c3f6763164a558fa123af26a9d208ca5689eeedec2574b5cd12a9d83cf958f21571212f + "@polkadot-api/json-rpc-provider": "npm:0.0.4" + "@polkadot-api/json-rpc-provider-proxy": "npm:0.2.4" + ws: "npm:^8.18.0" + checksum: 10c0/902c599ef02ddc3564cd4517aa3235262de1523a6879f81f94d73c90c260990fd595db7eacb2184670d9aa811eddc5eb346e78f74a60677846d991457e014ce9 languageName: node linkType: hard -"@polkadot/api-derive@npm:10.13.1": - version: 10.13.1 - resolution: "@polkadot/api-derive@npm:10.13.1" +"@polkadot/api-augment@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/api-augment@npm:12.4.2" dependencies: - "@polkadot/api": "npm:10.13.1" - "@polkadot/api-augment": "npm:10.13.1" - "@polkadot/api-base": "npm:10.13.1" - "@polkadot/rpc-core": "npm:10.13.1" - "@polkadot/types": "npm:10.13.1" - "@polkadot/types-codec": "npm:10.13.1" - "@polkadot/util": "npm:^12.6.2" - "@polkadot/util-crypto": "npm:^12.6.2" - rxjs: "npm:^7.8.1" - tslib: "npm:^2.6.2" - checksum: 10c0/91df2f399b0133bdcc19edb595e058481b1ffc63e80dbbc58c928eb04982cae1372fe7e3eda6b05888c88b68ae152f46646d080f4b940a5cca9307a681b7c78f + "@polkadot/api-base": "npm:12.4.2" + "@polkadot/rpc-augment": "npm:12.4.2" + "@polkadot/types": "npm:12.4.2" + "@polkadot/types-augment": "npm:12.4.2" + "@polkadot/types-codec": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" + tslib: "npm:^2.6.3" + checksum: 10c0/4cab0d50cde43eb25d830fca93cdfe10198302d44a271aacfa274d171671ce4fa4b07bafd0886f8cf78ce10478762e337e9fe27a652080a1dc6f5823be05bcb3 languageName: node linkType: hard -"@polkadot/api-derive@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/api-derive@npm:14.3.1" +"@polkadot/api-base@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/api-base@npm:12.4.2" dependencies: - "@polkadot/api": "npm:14.3.1" - "@polkadot/api-augment": "npm:14.3.1" - "@polkadot/api-base": "npm:14.3.1" - "@polkadot/rpc-core": "npm:14.3.1" - "@polkadot/types": "npm:14.3.1" - "@polkadot/types-codec": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - "@polkadot/util-crypto": "npm:^13.2.3" + "@polkadot/rpc-core": "npm:12.4.2" + "@polkadot/types": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" rxjs: "npm:^7.8.1" - tslib: "npm:^2.8.0" - checksum: 10c0/7939b7580dfe4469b72f1d2b006ab6a3158ab65c779b4e42e94e3d7b449b04bad0f995b18bc4dbb28f2d03e64762a93ad6391faa3c2809ae0993d0973bdd9dc3 + tslib: "npm:^2.6.3" + checksum: 10c0/b2e7b7a3f93070b09f5992f8128a378f47a8c808a08a2890c50abcaded040ab30e8b1b5c2b693c375f2187032dc8d010d407ac14e1e39fcb984a2e0f9f05e23d languageName: node linkType: hard -"@polkadot/api@npm:10.13.1, @polkadot/api@npm:^10.12.4, @polkadot/api@npm:^10.9.1": - version: 10.13.1 - resolution: "@polkadot/api@npm:10.13.1" +"@polkadot/api-derive@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/api-derive@npm:12.4.2" dependencies: - "@polkadot/api-augment": "npm:10.13.1" - "@polkadot/api-base": "npm:10.13.1" - "@polkadot/api-derive": "npm:10.13.1" - "@polkadot/keyring": "npm:^12.6.2" - "@polkadot/rpc-augment": "npm:10.13.1" - "@polkadot/rpc-core": "npm:10.13.1" - "@polkadot/rpc-provider": "npm:10.13.1" - "@polkadot/types": "npm:10.13.1" - "@polkadot/types-augment": "npm:10.13.1" - "@polkadot/types-codec": "npm:10.13.1" - "@polkadot/types-create": "npm:10.13.1" - "@polkadot/types-known": "npm:10.13.1" - "@polkadot/util": "npm:^12.6.2" - "@polkadot/util-crypto": "npm:^12.6.2" - eventemitter3: "npm:^5.0.1" + "@polkadot/api": "npm:12.4.2" + "@polkadot/api-augment": "npm:12.4.2" + "@polkadot/api-base": "npm:12.4.2" + "@polkadot/rpc-core": "npm:12.4.2" + "@polkadot/types": "npm:12.4.2" + "@polkadot/types-codec": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" + "@polkadot/util-crypto": "npm:^13.0.2" rxjs: "npm:^7.8.1" - tslib: "npm:^2.6.2" - checksum: 10c0/c2192c51aca790b2d8915a08e55e3b0461c49a1ce3ceccc5597cd74d68367ff0a881a0a864a0897835364482618d0a945247576573ef7c00688f1a25a5347ccc - languageName: node - linkType: hard - -"@polkadot/api@npm:14.3.1, @polkadot/api@npm:^14.3.1": - version: 14.3.1 - resolution: "@polkadot/api@npm:14.3.1" - dependencies: - "@polkadot/api-augment": "npm:14.3.1" - "@polkadot/api-base": "npm:14.3.1" - "@polkadot/api-derive": "npm:14.3.1" - "@polkadot/keyring": "npm:^13.2.3" - "@polkadot/rpc-augment": "npm:14.3.1" - "@polkadot/rpc-core": "npm:14.3.1" - "@polkadot/rpc-provider": "npm:14.3.1" - "@polkadot/types": "npm:14.3.1" - "@polkadot/types-augment": "npm:14.3.1" - "@polkadot/types-codec": "npm:14.3.1" - "@polkadot/types-create": "npm:14.3.1" - "@polkadot/types-known": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - "@polkadot/util-crypto": "npm:^13.2.3" + tslib: "npm:^2.6.3" + checksum: 10c0/47f0698fa2b3f1adc03af93fbf7c533d8801bad52c7d8989ab019ce215bbd1c1e8d7a0c18d89839e86619ae9e93385cb7963feecc323dad79751de1496d0c23c + languageName: node + linkType: hard + +"@polkadot/api@npm:12.4.2, @polkadot/api@npm:^12.0.2": + version: 12.4.2 + resolution: "@polkadot/api@npm:12.4.2" + dependencies: + "@polkadot/api-augment": "npm:12.4.2" + "@polkadot/api-base": "npm:12.4.2" + "@polkadot/api-derive": "npm:12.4.2" + "@polkadot/keyring": "npm:^13.0.2" + "@polkadot/rpc-augment": "npm:12.4.2" + "@polkadot/rpc-core": "npm:12.4.2" + "@polkadot/rpc-provider": "npm:12.4.2" + "@polkadot/types": "npm:12.4.2" + "@polkadot/types-augment": "npm:12.4.2" + "@polkadot/types-codec": "npm:12.4.2" + "@polkadot/types-create": "npm:12.4.2" + "@polkadot/types-known": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" + "@polkadot/util-crypto": "npm:^13.0.2" eventemitter3: "npm:^5.0.1" rxjs: "npm:^7.8.1" - tslib: "npm:^2.8.0" - checksum: 10c0/5c893cef918e9914c833c03b5f9fea86673cd2e50f8bdccb913177119c83124b036d69cdc8f52fd8d999b43f2da26a5260a8812cc516cba21b3fa727bd5e8023 + tslib: "npm:^2.6.3" + checksum: 10c0/64e9713508193a971e7fe830d39c625101b9dc9ce987ee38118d9a2e7e913eaeb9231cd7776b4d00ac293b23052c12ca6e7867fddacb8f7ec283017ee68ad1aa languageName: node linkType: hard -"@polkadot/extension-inject@npm:0.46.9, @polkadot/extension-inject@npm:^0.46.5": - version: 0.46.9 - resolution: "@polkadot/extension-inject@npm:0.46.9" +"@polkadot/extension-inject@npm:0.48.2": + version: 0.48.2 + resolution: "@polkadot/extension-inject@npm:0.48.2" dependencies: - "@polkadot/api": "npm:^10.12.4" - "@polkadot/rpc-provider": "npm:^10.12.4" - "@polkadot/types": "npm:^10.12.4" + "@polkadot/api": "npm:^12.0.2" + "@polkadot/rpc-provider": "npm:^12.0.2" + "@polkadot/types": "npm:^12.0.2" "@polkadot/util": "npm:^12.6.2" "@polkadot/util-crypto": "npm:^12.6.2" "@polkadot/x-global": "npm:^12.6.2" @@ -1968,7 +2242,7 @@ __metadata: peerDependencies: "@polkadot/api": "*" "@polkadot/util": "*" - checksum: 10c0/6afd3f8f5b1b803004eb50ab4588035c679933533042010cf55f685d21d8f34e2d3c8644f61831098b0cbd1abe8a669b48c22a1d19d0cc06175e9ff798e9a87c + checksum: 10c0/c8281942bf30f31c9b5388475483197e023a113da839b9144d8c558124728e7edd48b305e9f2d7f9674a3f0b6517571b6993eb0f4d40157a796bd772094e7ca3 languageName: node linkType: hard @@ -1986,7 +2260,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/keyring@npm:^13.2.3": +"@polkadot/keyring@npm:^13.0.2": version: 13.2.3 resolution: "@polkadot/keyring@npm:13.2.3" dependencies: @@ -2000,7 +2274,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/networks@npm:12.6.2, @polkadot/networks@npm:^12.6.2": +"@polkadot/networks@npm:12.6.2": version: 12.6.2 resolution: "@polkadot/networks@npm:12.6.2" dependencies: @@ -2011,7 +2285,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/networks@npm:13.2.3, @polkadot/networks@npm:^13.2.3": +"@polkadot/networks@npm:13.2.3, @polkadot/networks@npm:^13.0.2": version: 13.2.3 resolution: "@polkadot/networks@npm:13.2.3" dependencies: @@ -2022,253 +2296,178 @@ __metadata: languageName: node linkType: hard -"@polkadot/rpc-augment@npm:10.13.1": - version: 10.13.1 - resolution: "@polkadot/rpc-augment@npm:10.13.1" - dependencies: - "@polkadot/rpc-core": "npm:10.13.1" - "@polkadot/types": "npm:10.13.1" - "@polkadot/types-codec": "npm:10.13.1" - "@polkadot/util": "npm:^12.6.2" - tslib: "npm:^2.6.2" - checksum: 10c0/5389ac83712cb0144e5f6b4319f76a54e8c85d455043ba688d32b233bc83202479f5506a8c8a0a7b0e8688150ca4c8d63ef57b2255e52827a5738eb600ab01ee - languageName: node - linkType: hard - -"@polkadot/rpc-augment@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/rpc-augment@npm:14.3.1" - dependencies: - "@polkadot/rpc-core": "npm:14.3.1" - "@polkadot/types": "npm:14.3.1" - "@polkadot/types-codec": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - tslib: "npm:^2.8.0" - checksum: 10c0/9a55690656c3e8154745439e1bc7f0e9d29af19d20374724bc9f21400540efe6f5d94bb18faf5336061e16f86afdeecf0d5f3e7b0ccfad5b87f602c5633d4743 - languageName: node - linkType: hard - -"@polkadot/rpc-core@npm:10.13.1": - version: 10.13.1 - resolution: "@polkadot/rpc-core@npm:10.13.1" +"@polkadot/rpc-augment@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/rpc-augment@npm:12.4.2" dependencies: - "@polkadot/rpc-augment": "npm:10.13.1" - "@polkadot/rpc-provider": "npm:10.13.1" - "@polkadot/types": "npm:10.13.1" - "@polkadot/util": "npm:^12.6.2" - rxjs: "npm:^7.8.1" - tslib: "npm:^2.6.2" - checksum: 10c0/423479b6332eae4729076e1faa7371e7516021348ec6b4a4323a25740ea2577afdb395e767580babd854da14f63065cd4ac766a58b61263d09db64f1f6bb2599 + "@polkadot/rpc-core": "npm:12.4.2" + "@polkadot/types": "npm:12.4.2" + "@polkadot/types-codec": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" + tslib: "npm:^2.6.3" + checksum: 10c0/8e078db8496337c16bfb474cb557aaed5cccb2c1a3b8a56ad729fea308b23745c0cf5db10212f5653b60344add2084fc5ac7521a7b08c19fd309280e539336cf languageName: node linkType: hard -"@polkadot/rpc-core@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/rpc-core@npm:14.3.1" +"@polkadot/rpc-core@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/rpc-core@npm:12.4.2" dependencies: - "@polkadot/rpc-augment": "npm:14.3.1" - "@polkadot/rpc-provider": "npm:14.3.1" - "@polkadot/types": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" + "@polkadot/rpc-augment": "npm:12.4.2" + "@polkadot/rpc-provider": "npm:12.4.2" + "@polkadot/types": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" rxjs: "npm:^7.8.1" - tslib: "npm:^2.8.0" - checksum: 10c0/439223156cb243e5907655057367178562cdc4a30f960d67a61705b638cc017814055e0086a28e4b06b9b88e382302a7c17b1020b5a09f29dc37233ed2d7ffde - languageName: node - linkType: hard - -"@polkadot/rpc-provider@npm:10.13.1, @polkadot/rpc-provider@npm:^10.12.4": - version: 10.13.1 - resolution: "@polkadot/rpc-provider@npm:10.13.1" - dependencies: - "@polkadot/keyring": "npm:^12.6.2" - "@polkadot/types": "npm:10.13.1" - "@polkadot/types-support": "npm:10.13.1" - "@polkadot/util": "npm:^12.6.2" - "@polkadot/util-crypto": "npm:^12.6.2" - "@polkadot/x-fetch": "npm:^12.6.2" - "@polkadot/x-global": "npm:^12.6.2" - "@polkadot/x-ws": "npm:^12.6.2" - "@substrate/connect": "npm:0.8.8" - eventemitter3: "npm:^5.0.1" - mock-socket: "npm:^9.3.1" - nock: "npm:^13.5.0" - tslib: "npm:^2.6.2" - dependenciesMeta: - "@substrate/connect": - optional: true - checksum: 10c0/13bc88d973a55b131bea9429831caa19b85cde73bb0c8d124bbeb1f9b5cd63d4e297a38be477e75b8930e181ab837249fec06417dc52cc5c438149313e5a2947 + tslib: "npm:^2.6.3" + checksum: 10c0/4201b1d503801a672f2ceb0e6ab90226eb03c2d668879669656d73a952c556ba32e0a85c479d87ccd0aa80ce8fbc69ddde69abee462517dabc4736b3993deca6 languageName: node linkType: hard -"@polkadot/rpc-provider@npm:14.3.1, @polkadot/rpc-provider@npm:^14.3.1": - version: 14.3.1 - resolution: "@polkadot/rpc-provider@npm:14.3.1" +"@polkadot/rpc-provider@npm:12.4.2, @polkadot/rpc-provider@npm:^12.0.2": + version: 12.4.2 + resolution: "@polkadot/rpc-provider@npm:12.4.2" dependencies: - "@polkadot/keyring": "npm:^13.2.3" - "@polkadot/types": "npm:14.3.1" - "@polkadot/types-support": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - "@polkadot/util-crypto": "npm:^13.2.3" - "@polkadot/x-fetch": "npm:^13.2.3" - "@polkadot/x-global": "npm:^13.2.3" - "@polkadot/x-ws": "npm:^13.2.3" + "@polkadot/keyring": "npm:^13.0.2" + "@polkadot/types": "npm:12.4.2" + "@polkadot/types-support": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" + "@polkadot/util-crypto": "npm:^13.0.2" + "@polkadot/x-fetch": "npm:^13.0.2" + "@polkadot/x-global": "npm:^13.0.2" + "@polkadot/x-ws": "npm:^13.0.2" "@substrate/connect": "npm:0.8.11" eventemitter3: "npm:^5.0.1" mock-socket: "npm:^9.3.1" - nock: "npm:^13.5.5" - tslib: "npm:^2.8.0" + nock: "npm:^13.5.4" + tslib: "npm:^2.6.3" dependenciesMeta: "@substrate/connect": optional: true - checksum: 10c0/cab0c873be695678f4e2086dd0e7ba3e79e3aa20f447f941a60f160d3a8f52bfa340c54b10e0e0cd8bdcdd57d8effcab230d31b9bec1e6432aa92b19e881f41c + checksum: 10c0/59968dfae8ecaed840ec61c84d50953faf14d76dbcfcf61ef4acb88c9f4ef07c0c2b9c5227cb2b63801a0895d3f10edb686ae3126269eda5635f29796fa20fdf languageName: node linkType: hard -"@polkadot/types-augment@npm:10.13.1, @polkadot/types-augment@npm:^10.9.1": - version: 10.13.1 - resolution: "@polkadot/types-augment@npm:10.13.1" +"@polkadot/types-augment@npm:12.1.1": + version: 12.1.1 + resolution: "@polkadot/types-augment@npm:12.1.1" dependencies: - "@polkadot/types": "npm:10.13.1" - "@polkadot/types-codec": "npm:10.13.1" + "@polkadot/types": "npm:12.1.1" + "@polkadot/types-codec": "npm:12.1.1" "@polkadot/util": "npm:^12.6.2" tslib: "npm:^2.6.2" - checksum: 10c0/0686a834fd5d4db1cc74c184057cf1c47f008d3d541866cb2f2a38464c6a41cb159e5ec914b2e3d68511f6c6c7798238b58ec3bd1315a67fbb1ee073147457c6 + checksum: 10c0/20342a217823e039f3c0cc011319429345b98472230472e3ec3563d97581ad2a20a974cdaf66c8ce2d49381a5cdf759326027b6f55bef18b396decfd0db37030 languageName: node linkType: hard -"@polkadot/types-augment@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/types-augment@npm:14.3.1" +"@polkadot/types-augment@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/types-augment@npm:12.4.2" dependencies: - "@polkadot/types": "npm:14.3.1" - "@polkadot/types-codec": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - tslib: "npm:^2.8.0" - checksum: 10c0/56192ce0bd656343196c46cea114ddf61488307ac8e4c7d578f6e41b877fc258765f1408ebe408ca9a0f7f64b16f661776fd76bb215f2d74c9690e4d4855de72 + "@polkadot/types": "npm:12.4.2" + "@polkadot/types-codec": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" + tslib: "npm:^2.6.3" + checksum: 10c0/9dcae5ec9fd7aaac9d3ffe2f5adb9b5c4704376018db4860215ca38805b189c5ef2f90360da0ff29d8b9a9715617bb5eabf6870bcfd8f9eeba974d6eb9b5bfab languageName: node linkType: hard -"@polkadot/types-codec@npm:10.13.1": - version: 10.13.1 - resolution: "@polkadot/types-codec@npm:10.13.1" +"@polkadot/types-codec@npm:12.1.1": + version: 12.1.1 + resolution: "@polkadot/types-codec@npm:12.1.1" dependencies: "@polkadot/util": "npm:^12.6.2" "@polkadot/x-bigint": "npm:^12.6.2" tslib: "npm:^2.6.2" - checksum: 10c0/8a35c492006502804a5531231c14037ab98a13f345f4e550142254e69d62d451f0caa89347ac689b92f90b582fe6ab2f1c8eca30cdf327951323b6400fca2e50 - languageName: node - linkType: hard - -"@polkadot/types-codec@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/types-codec@npm:14.3.1" - dependencies: - "@polkadot/util": "npm:^13.2.3" - "@polkadot/x-bigint": "npm:^13.2.3" - tslib: "npm:^2.8.0" - checksum: 10c0/e60353aa1218b460167d97447a7edbab527cceeb505e8206c065890622db5e95c6b1e703c69d75fd2dad410c8c598b0f721995f5d1af357420b461038526641b - languageName: node - linkType: hard - -"@polkadot/types-create@npm:10.13.1": - version: 10.13.1 - resolution: "@polkadot/types-create@npm:10.13.1" - dependencies: - "@polkadot/types-codec": "npm:10.13.1" - "@polkadot/util": "npm:^12.6.2" - tslib: "npm:^2.6.2" - checksum: 10c0/efe57d84f6088111b53d29db07ab9bf951f79c3e9b4875882c7a9bb0a6f1e00230e63a84cf2a850528119bbfa7f30bdfb21bba645e3922d88ff83092ea0350c5 + checksum: 10c0/d717a4ad746c5748c683fe449814a85e0c5f4b2530c2448f5950918c83f43370bdedd51ea9c5f31da8992f674c05324649f2e1d350d5b688316be345e2257893 languageName: node linkType: hard -"@polkadot/types-create@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/types-create@npm:14.3.1" +"@polkadot/types-codec@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/types-codec@npm:12.4.2" dependencies: - "@polkadot/types-codec": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - tslib: "npm:^2.8.0" - checksum: 10c0/18b07ebe2140010426f9c9c6648bd4e56911fc52465356c89b9969572db1c97a2f80bea985eb6f90d07bf1ca38b690050e36dc725ec5fcf4edeef85eabbe381a + "@polkadot/util": "npm:^13.0.2" + "@polkadot/x-bigint": "npm:^13.0.2" + tslib: "npm:^2.6.3" + checksum: 10c0/2a56694e6998fc2afbe4fe8a9f9805eb251e880f1343af380f70c42965d30bae2249e5a5f346e675d5f78173770ebd4fa0758ed8b9f1397db8183eb686d11842 languageName: node linkType: hard -"@polkadot/types-known@npm:10.13.1": - version: 10.13.1 - resolution: "@polkadot/types-known@npm:10.13.1" +"@polkadot/types-create@npm:12.1.1": + version: 12.1.1 + resolution: "@polkadot/types-create@npm:12.1.1" dependencies: - "@polkadot/networks": "npm:^12.6.2" - "@polkadot/types": "npm:10.13.1" - "@polkadot/types-codec": "npm:10.13.1" - "@polkadot/types-create": "npm:10.13.1" + "@polkadot/types-codec": "npm:12.1.1" "@polkadot/util": "npm:^12.6.2" tslib: "npm:^2.6.2" - checksum: 10c0/ce4e5b402e4d923a1b93475997e57bb6b7f95cc679a466c7d563d9d033872479100cf5ac72c58faaf57610602c913555ef0b4bd4672c6c76fc733c062d41959b + checksum: 10c0/b4e3e4f93626f816a104868467b5299a19416b14b6783b576c498636d46a7d86488d623a91d5b3f106e4f7717959c90e1b9d0a65f2a60893ecb4f491ea5c8b54 languageName: node linkType: hard -"@polkadot/types-known@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/types-known@npm:14.3.1" +"@polkadot/types-create@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/types-create@npm:12.4.2" dependencies: - "@polkadot/networks": "npm:^13.2.3" - "@polkadot/types": "npm:14.3.1" - "@polkadot/types-codec": "npm:14.3.1" - "@polkadot/types-create": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - tslib: "npm:^2.8.0" - checksum: 10c0/c290b290b4856c77b15bc29a177caeaa6ce408b48c36a6c1fdf38160c5573a95bdd1476a1ad085dbe0853684deb4cd4c49fa67ee2d120809de8ec6e8005d08eb + "@polkadot/types-codec": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" + tslib: "npm:^2.6.3" + checksum: 10c0/c075d07c2d3212f0ab9772cd008bfadccde7a35f6366c6704a326f8e5199fce7e7eb7959a6bd229b69fcbc3900c522892f94b08b4cd991be6e42f2a786585d0f languageName: node linkType: hard -"@polkadot/types-support@npm:10.13.1": - version: 10.13.1 - resolution: "@polkadot/types-support@npm:10.13.1" +"@polkadot/types-known@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/types-known@npm:12.4.2" dependencies: - "@polkadot/util": "npm:^12.6.2" - tslib: "npm:^2.6.2" - checksum: 10c0/a9bf65b139b1c861acc198ce650e14d2014f88d30f890710baf3481caa16b44dc39122b05d34cc86211b08e082cf4e7d5680ba3a4008711fe86b70d62a65219c + "@polkadot/networks": "npm:^13.0.2" + "@polkadot/types": "npm:12.4.2" + "@polkadot/types-codec": "npm:12.4.2" + "@polkadot/types-create": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" + tslib: "npm:^2.6.3" + checksum: 10c0/2e7fdd895c6478e102b91d7cb3428b75c34aa0b037ba9d2d2b97d350a19ed7bd6ea4f2c91c52eef4039ebec330fd1cd58a0f88fc30882c2a6623c0cae78b421a languageName: node linkType: hard -"@polkadot/types-support@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/types-support@npm:14.3.1" +"@polkadot/types-support@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/types-support@npm:12.4.2" dependencies: - "@polkadot/util": "npm:^13.2.3" - tslib: "npm:^2.8.0" - checksum: 10c0/fec80615ee0cdfa9d70a9e73d598de5b58f01f76557ac9c0060f9036bac1dd3695184ce8e4a81461a83988da2c28e5a8248a941fc891b2606a681f1ef218adfa + "@polkadot/util": "npm:^13.0.2" + tslib: "npm:^2.6.3" + checksum: 10c0/0277fe88cac0f2447b3655bb5ca32dfe7dc7e35a82d22873baf95a0272a207f73853e94b3340e7e9230945dfaa8f14f93f4ffb13c8b29d449f602e8c5fe3f3c2 languageName: node linkType: hard -"@polkadot/types@npm:10.13.1, @polkadot/types@npm:^10.11.2, @polkadot/types@npm:^10.12.4": - version: 10.13.1 - resolution: "@polkadot/types@npm:10.13.1" +"@polkadot/types@npm:12.1.1": + version: 12.1.1 + resolution: "@polkadot/types@npm:12.1.1" dependencies: "@polkadot/keyring": "npm:^12.6.2" - "@polkadot/types-augment": "npm:10.13.1" - "@polkadot/types-codec": "npm:10.13.1" - "@polkadot/types-create": "npm:10.13.1" + "@polkadot/types-augment": "npm:12.1.1" + "@polkadot/types-codec": "npm:12.1.1" + "@polkadot/types-create": "npm:12.1.1" "@polkadot/util": "npm:^12.6.2" "@polkadot/util-crypto": "npm:^12.6.2" rxjs: "npm:^7.8.1" tslib: "npm:^2.6.2" - checksum: 10c0/1fe50d7ba10368dd944fec0e2da740f8c665c1c417362ab5ed1587d9083259c65064e04a862443d1a6c9f1da86b8dee6a4945e711064f0318895a38ad1303e3b + checksum: 10c0/46395b18dc78d7d8295df6a80fc8a00d73d942ffa2ef8bd7fc6591ac3d018f70c0765ac54b848c508514196c7bb302fc08ac80779ca1431f57c423b20c7080ac languageName: node linkType: hard -"@polkadot/types@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/types@npm:14.3.1" +"@polkadot/types@npm:12.4.2, @polkadot/types@npm:^12.0.2": + version: 12.4.2 + resolution: "@polkadot/types@npm:12.4.2" dependencies: - "@polkadot/keyring": "npm:^13.2.3" - "@polkadot/types-augment": "npm:14.3.1" - "@polkadot/types-codec": "npm:14.3.1" - "@polkadot/types-create": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - "@polkadot/util-crypto": "npm:^13.2.3" + "@polkadot/keyring": "npm:^13.0.2" + "@polkadot/types-augment": "npm:12.4.2" + "@polkadot/types-codec": "npm:12.4.2" + "@polkadot/types-create": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" + "@polkadot/util-crypto": "npm:^13.0.2" rxjs: "npm:^7.8.1" - tslib: "npm:^2.8.0" - checksum: 10c0/1cf0ad0f5f51cce1f9f3a5a694669fe327e3b8ec2c277dd7f0be74c7df1147b8713651c3a99ad8ac9a8c125a7681c2d6d05b6a7b0eaaa96f96a52bd4c61a8a47 + tslib: "npm:^2.6.3" + checksum: 10c0/bdea1658a367678a158d5d6ba6224a081cfd5fb38b6d56c360321e40628a23261261c869e7ab1ac0c89c0140777f532963c46999e5fb0f13233599a32eabdf99 languageName: node linkType: hard @@ -2292,7 +2491,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/util-crypto@npm:13.2.3, @polkadot/util-crypto@npm:^13.1.1, @polkadot/util-crypto@npm:^13.2.3": +"@polkadot/util-crypto@npm:13.2.3, @polkadot/util-crypto@npm:^13.0.2": version: 13.2.3 resolution: "@polkadot/util-crypto@npm:13.2.3" dependencies: @@ -2327,7 +2526,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/util@npm:13.2.3, @polkadot/util@npm:^13.1.1, @polkadot/util@npm:^13.2.3": +"@polkadot/util@npm:13.2.3, @polkadot/util@npm:^13.0.2, @polkadot/util@npm:^13.2.3": version: 13.2.3 resolution: "@polkadot/util@npm:13.2.3" dependencies: @@ -2432,7 +2631,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/x-bigint@npm:13.2.3, @polkadot/x-bigint@npm:^13.2.3": +"@polkadot/x-bigint@npm:13.2.3, @polkadot/x-bigint@npm:^13.0.2": version: 13.2.3 resolution: "@polkadot/x-bigint@npm:13.2.3" dependencies: @@ -2442,18 +2641,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/x-fetch@npm:^12.6.2": - version: 12.6.2 - resolution: "@polkadot/x-fetch@npm:12.6.2" - dependencies: - "@polkadot/x-global": "npm:12.6.2" - node-fetch: "npm:^3.3.2" - tslib: "npm:^2.6.2" - checksum: 10c0/c4e34c28f4374db3b6795b31f63434b4241896a82cd1a0aa81196c7dbe8aa345069a39d27d5c3af214d8d2824154c6fe1fcbe9cb22af32f9a2c3fd22dc4b8583 - languageName: node - linkType: hard - -"@polkadot/x-fetch@npm:^13.2.3": +"@polkadot/x-fetch@npm:^13.0.2": version: 13.2.3 resolution: "@polkadot/x-fetch@npm:13.2.3" dependencies: @@ -2473,7 +2661,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/x-global@npm:13.2.3, @polkadot/x-global@npm:^13.2.3": +"@polkadot/x-global@npm:13.2.3, @polkadot/x-global@npm:^13.0.2": version: 13.2.3 resolution: "@polkadot/x-global@npm:13.2.3" dependencies: @@ -2548,18 +2736,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/x-ws@npm:^12.6.2": - version: 12.6.2 - resolution: "@polkadot/x-ws@npm:12.6.2" - dependencies: - "@polkadot/x-global": "npm:12.6.2" - tslib: "npm:^2.6.2" - ws: "npm:^8.15.1" - checksum: 10c0/15565803a34aa7d6654c4c05725f5f44e504caa69f590523c5569fcbd66cf1e467de03e3e13a4d71bb60efceb28c60fd5719bee5efd721c020cf470025bbeb29 - languageName: node - linkType: hard - -"@polkadot/x-ws@npm:^13.2.3": +"@polkadot/x-ws@npm:^13.0.2": version: 13.2.3 resolution: "@polkadot/x-ws@npm:13.2.3" dependencies: @@ -2570,12 +2747,13 @@ __metadata: languageName: node linkType: hard -"@polkagate/extension-dapp@npm:^0.46.13": - version: 0.46.13 - resolution: "@polkagate/extension-dapp@npm:0.46.13" +"@polkagate/extension-dapp@npm:^0.48.2": + version: 0.48.2 + resolution: "@polkagate/extension-dapp@npm:0.48.2" dependencies: - "@polkadot/extension-inject": "npm:0.46.9" - "@polkadot/types": "npm:^10.11.2" + "@metamask/utils": "npm:^9.0.0" + "@polkadot/extension-inject": "npm:0.48.2" + "@polkadot/types": "npm:12.1.1" "@polkadot/util": "npm:^12.6.2" "@polkadot/util-crypto": "npm:^12.6.2" tslib: "npm:^2.6.2" @@ -2583,7 +2761,7 @@ __metadata: "@polkadot/api": "*" "@polkadot/util": "*" "@polkadot/util-crypto": "*" - checksum: 10c0/fecad5ba8ed04c7a745b5870c75d67636dd8bbe8230a809aad443d73dcac3ba4d0bc4ba164317564f1e1fcb425e47c0c2189d5ada583e39975ec64b47254fa17 + checksum: 10c0/87dc06ff885533135ee4fde2f74bb9809ce3ed140fe2e3baef999831537ae3c537e014140f9c8e49cdf32c1f912194b58970e42161cd9f4210dcbca5cedc35f1 languageName: node linkType: hard @@ -2672,128 +2850,128 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-android-arm-eabi@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-android-arm-eabi@npm:4.26.0" +"@rollup/rollup-android-arm-eabi@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.27.4" conditions: os=android & cpu=arm languageName: node linkType: hard -"@rollup/rollup-android-arm64@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-android-arm64@npm:4.26.0" +"@rollup/rollup-android-arm64@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-android-arm64@npm:4.27.4" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-arm64@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-darwin-arm64@npm:4.26.0" +"@rollup/rollup-darwin-arm64@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-darwin-arm64@npm:4.27.4" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-x64@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-darwin-x64@npm:4.26.0" +"@rollup/rollup-darwin-x64@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-darwin-x64@npm:4.27.4" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-freebsd-arm64@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-freebsd-arm64@npm:4.26.0" +"@rollup/rollup-freebsd-arm64@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.27.4" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-freebsd-x64@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-freebsd-x64@npm:4.26.0" +"@rollup/rollup-freebsd-x64@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-freebsd-x64@npm:4.27.4" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-linux-arm-gnueabihf@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.26.0" +"@rollup/rollup-linux-arm-gnueabihf@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.27.4" conditions: os=linux & cpu=arm & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm-musleabihf@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.26.0" +"@rollup/rollup-linux-arm-musleabihf@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.27.4" conditions: os=linux & cpu=arm & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-arm64-gnu@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.26.0" +"@rollup/rollup-linux-arm64-gnu@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.27.4" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm64-musl@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-linux-arm64-musl@npm:4.26.0" +"@rollup/rollup-linux-arm64-musl@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.27.4" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-powerpc64le-gnu@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.26.0" +"@rollup/rollup-linux-powerpc64le-gnu@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.27.4" conditions: os=linux & cpu=ppc64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-riscv64-gnu@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.26.0" +"@rollup/rollup-linux-riscv64-gnu@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.27.4" conditions: os=linux & cpu=riscv64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-s390x-gnu@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.26.0" +"@rollup/rollup-linux-s390x-gnu@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.27.4" conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-gnu@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-linux-x64-gnu@npm:4.26.0" +"@rollup/rollup-linux-x64-gnu@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.27.4" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-musl@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-linux-x64-musl@npm:4.26.0" +"@rollup/rollup-linux-x64-musl@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.27.4" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-win32-arm64-msvc@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.26.0" +"@rollup/rollup-win32-arm64-msvc@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.27.4" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-win32-ia32-msvc@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.26.0" +"@rollup/rollup-win32-ia32-msvc@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.27.4" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rollup/rollup-win32-x64-msvc@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-win32-x64-msvc@npm:4.26.0" +"@rollup/rollup-win32-x64-msvc@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.27.4" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -2826,13 +3004,20 @@ __metadata: linkType: hard "@safe-global/safe-gateway-typescript-sdk@npm:^3.5.3": - version: 3.22.2 - resolution: "@safe-global/safe-gateway-typescript-sdk@npm:3.22.2" - checksum: 10c0/4c61c6bc1e720ceb98e7812ef060e40120e130c385f1ac8012a99155179b0651e12f608e053c9e4d1d7917881920e9e3b15c3c90805f9bbb7f28d80b13d04381 + version: 3.22.4 + resolution: "@safe-global/safe-gateway-typescript-sdk@npm:3.22.4" + checksum: 10c0/9080ae01254571d0f62b35764a1ca4fec302627c467060fa1427127606415627bd8c3d0909e0c0410b64110c445b6f299f5b0e9228268e1213dd75f21a6139ef + languageName: node + linkType: hard + +"@scure/base@npm:^1.1.1, @scure/base@npm:^1.1.3, @scure/base@npm:^1.1.5, @scure/base@npm:^1.1.7, @scure/base@npm:^1.1.9, @scure/base@npm:~1.2.1": + version: 1.2.1 + resolution: "@scure/base@npm:1.2.1" + checksum: 10c0/e61068854370855b89c50c28fa4092ea6780f1e0db64ea94075ab574ebcc964f719a3120dc708db324991f4b3e652d92ebda03fce2bf6a4900ceeacf9c0ff933 languageName: node linkType: hard -"@scure/base@npm:^1.1.1, @scure/base@npm:^1.1.3, @scure/base@npm:^1.1.5, @scure/base@npm:^1.1.7, @scure/base@npm:~1.1.6, @scure/base@npm:~1.1.7, @scure/base@npm:~1.1.8": +"@scure/base@npm:~1.1.6, @scure/base@npm:~1.1.7, @scure/base@npm:~1.1.8": version: 1.1.9 resolution: "@scure/base@npm:1.1.9" checksum: 10c0/77a06b9a2db8144d22d9bf198338893d77367c51b58c72b99df990c0a11f7cadd066d4102abb15e3ca6798d1529e3765f55c4355742465e49aed7a0c01fe76e8 @@ -2850,7 +3035,7 @@ __metadata: languageName: node linkType: hard -"@scure/bip32@npm:1.5.0, @scure/bip32@npm:^1.5.0": +"@scure/bip32@npm:1.5.0": version: 1.5.0 resolution: "@scure/bip32@npm:1.5.0" dependencies: @@ -2861,6 +3046,17 @@ __metadata: languageName: node linkType: hard +"@scure/bip32@npm:^1.5.0": + version: 1.6.0 + resolution: "@scure/bip32@npm:1.6.0" + dependencies: + "@noble/curves": "npm:~1.7.0" + "@noble/hashes": "npm:~1.6.0" + "@scure/base": "npm:~1.2.1" + checksum: 10c0/5a5eff8c0bc0b53d70528c5eda6efa7ed6d186a5c9ba0a339edf9c150ee3f331d837ffe29d2c6c6336b1f88ad90aa8b6e596a4950217343f36916d8024f79bdf + languageName: node + linkType: hard + "@scure/bip39@npm:1.3.0": version: 1.3.0 resolution: "@scure/bip39@npm:1.3.0" @@ -2871,7 +3067,7 @@ __metadata: languageName: node linkType: hard -"@scure/bip39@npm:1.4.0, @scure/bip39@npm:^1.4.0": +"@scure/bip39@npm:1.4.0": version: 1.4.0 resolution: "@scure/bip39@npm:1.4.0" dependencies: @@ -2881,6 +3077,23 @@ __metadata: languageName: node linkType: hard +"@scure/bip39@npm:^1.4.0": + version: 1.5.0 + resolution: "@scure/bip39@npm:1.5.0" + dependencies: + "@noble/hashes": "npm:~1.6.0" + "@scure/base": "npm:~1.2.1" + checksum: 10c0/114ab88fb00269d17a73d5c39a2cade47403e05f6df5a8d6f5da6e7f2b071966fe8f656a740dc3399acd006163f234e82b680544c38004703dbb60f8a29daf73 + languageName: node + linkType: hard + +"@sec-ant/readable-stream@npm:^0.4.1": + version: 0.4.1 + resolution: "@sec-ant/readable-stream@npm:0.4.1" + checksum: 10c0/64e9e9cf161e848067a5bf60cdc04d18495dc28bb63a8d9f8993e4dd99b91ad34e4b563c85de17d91ffb177ec17a0664991d2e115f6543e73236a906068987af + languageName: node + linkType: hard + "@sinclair/typebox@npm:^0.27.8": version: 0.27.8 resolution: "@sinclair/typebox@npm:0.27.8" @@ -2888,6 +3101,13 @@ __metadata: languageName: node linkType: hard +"@sindresorhus/merge-streams@npm:^4.0.0": + version: 4.0.0 + resolution: "@sindresorhus/merge-streams@npm:4.0.0" + checksum: 10c0/482ee543629aa1933b332f811a1ae805a213681ecdd98c042b1c1b89387df63e7812248bb4df3910b02b3cc5589d3d73e4393f30e197c9dde18046ccd471fc6b + languageName: node + linkType: hard + "@socket.io/component-emitter@npm:~3.1.0": version: 3.1.2 resolution: "@socket.io/component-emitter@npm:3.1.2" @@ -3072,10 +3292,10 @@ __metadata: languageName: node linkType: hard -"@substrate/connect-known-chains@npm:^1.1.1, @substrate/connect-known-chains@npm:^1.1.5, @substrate/connect-known-chains@npm:^1.7.0": - version: 1.7.0 - resolution: "@substrate/connect-known-chains@npm:1.7.0" - checksum: 10c0/454f115eed8c2299b9ba66c8c3328af80767c501fb15a4e54e6788cc5d9ab6289c91a44a215dbaab8df4fda41063bafff5eee3926b43cd8787af86610e45c673 +"@substrate/connect-known-chains@npm:^1.1.5, @substrate/connect-known-chains@npm:^1.8.0": + version: 1.8.0 + resolution: "@substrate/connect-known-chains@npm:1.8.0" + checksum: 10c0/9315ca8f6a7bfc2d06176c217be68a81109e32da6a90cda8f0827c8101fee6aa696264953c06646c2c450bf3adbae481df0ca1ea1988533c127a03fcec67827c languageName: node linkType: hard @@ -3091,27 +3311,15 @@ __metadata: languageName: node linkType: hard -"@substrate/connect@npm:0.8.8": - version: 0.8.8 - resolution: "@substrate/connect@npm:0.8.8" - dependencies: - "@substrate/connect-extension-protocol": "npm:^2.0.0" - "@substrate/connect-known-chains": "npm:^1.1.1" - "@substrate/light-client-extension-helpers": "npm:^0.0.4" - smoldot: "npm:2.0.22" - checksum: 10c0/d2507a5c2392523c31b1e25a4b0955b7c271406d527511c5ceab561fff0d8788262d638ff996dc2978464998f5db78cf373b34d2af86b040686c86f36093ec65 - languageName: node - linkType: hard - "@substrate/connect@npm:^2.0.1": - version: 2.0.1 - resolution: "@substrate/connect@npm:2.0.1" + version: 2.1.0 + resolution: "@substrate/connect@npm:2.1.0" dependencies: "@substrate/connect-extension-protocol": "npm:^2.2.1" - "@substrate/connect-known-chains": "npm:^1.7.0" + "@substrate/connect-known-chains": "npm:^1.8.0" "@substrate/smoldot-discovery": "npm:^2.0.1" - smoldot: "npm:2.0.30" - checksum: 10c0/b6e828a980cae0a07d6330fd68bb80f8c337fd1698a1aa19ff5117c5b67c60848678ef4a6a6fde55eb419e9c82c974d98eaa4cfd19834fcd641bf4a38d870ddc + smoldot: "npm:^2.0.34" + checksum: 10c0/2a754d5c553a99e20fecb5f57c74f7a589a0ae6ea70c7a7e19f31d9f546b019d827d32945c21164db9934c17b09f415821e73aca5e11f8c27e0651406c99c836 languageName: node linkType: hard @@ -3122,23 +3330,6 @@ __metadata: languageName: node linkType: hard -"@substrate/light-client-extension-helpers@npm:^0.0.4": - version: 0.0.4 - resolution: "@substrate/light-client-extension-helpers@npm:0.0.4" - dependencies: - "@polkadot-api/client": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - "@polkadot-api/json-rpc-provider": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - "@polkadot-api/json-rpc-provider-proxy": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - "@polkadot-api/substrate-client": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - "@substrate/connect-extension-protocol": "npm:^2.0.0" - "@substrate/connect-known-chains": "npm:^1.1.1" - rxjs: "npm:^7.8.1" - peerDependencies: - smoldot: 2.x - checksum: 10c0/51e44ab2f3efaf8482047d3ed89e51327b54f53bd9d4169ce3738daf22e0afa849560cfd1f487ae809ea9adfdb93812f1cb74329d44cf686c0b552493b47b122 - languageName: node - linkType: hard - "@substrate/light-client-extension-helpers@npm:^1.0.0": version: 1.0.0 resolution: "@substrate/light-client-extension-helpers@npm:1.0.0" @@ -3299,92 +3490,92 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-darwin-arm64@npm:1.9.2" +"@swc/core-darwin-arm64@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-darwin-arm64@npm:1.9.3" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-darwin-x64@npm:1.9.2" +"@swc/core-darwin-x64@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-darwin-x64@npm:1.9.3" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.9.2" +"@swc/core-linux-arm-gnueabihf@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.9.3" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-linux-arm64-gnu@npm:1.9.2" +"@swc/core-linux-arm64-gnu@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-linux-arm64-gnu@npm:1.9.3" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-linux-arm64-musl@npm:1.9.2" +"@swc/core-linux-arm64-musl@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-linux-arm64-musl@npm:1.9.3" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-linux-x64-gnu@npm:1.9.2" +"@swc/core-linux-x64-gnu@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-linux-x64-gnu@npm:1.9.3" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-linux-x64-musl@npm:1.9.2" +"@swc/core-linux-x64-musl@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-linux-x64-musl@npm:1.9.3" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-win32-arm64-msvc@npm:1.9.2" +"@swc/core-win32-arm64-msvc@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-win32-arm64-msvc@npm:1.9.3" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-win32-ia32-msvc@npm:1.9.2" +"@swc/core-win32-ia32-msvc@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-win32-ia32-msvc@npm:1.9.3" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-win32-x64-msvc@npm:1.9.2" +"@swc/core-win32-x64-msvc@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-win32-x64-msvc@npm:1.9.3" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.7.26": - version: 1.9.2 - resolution: "@swc/core@npm:1.9.2" - dependencies: - "@swc/core-darwin-arm64": "npm:1.9.2" - "@swc/core-darwin-x64": "npm:1.9.2" - "@swc/core-linux-arm-gnueabihf": "npm:1.9.2" - "@swc/core-linux-arm64-gnu": "npm:1.9.2" - "@swc/core-linux-arm64-musl": "npm:1.9.2" - "@swc/core-linux-x64-gnu": "npm:1.9.2" - "@swc/core-linux-x64-musl": "npm:1.9.2" - "@swc/core-win32-arm64-msvc": "npm:1.9.2" - "@swc/core-win32-ia32-msvc": "npm:1.9.2" - "@swc/core-win32-x64-msvc": "npm:1.9.2" + version: 1.9.3 + resolution: "@swc/core@npm:1.9.3" + dependencies: + "@swc/core-darwin-arm64": "npm:1.9.3" + "@swc/core-darwin-x64": "npm:1.9.3" + "@swc/core-linux-arm-gnueabihf": "npm:1.9.3" + "@swc/core-linux-arm64-gnu": "npm:1.9.3" + "@swc/core-linux-arm64-musl": "npm:1.9.3" + "@swc/core-linux-x64-gnu": "npm:1.9.3" + "@swc/core-linux-x64-musl": "npm:1.9.3" + "@swc/core-win32-arm64-msvc": "npm:1.9.3" + "@swc/core-win32-ia32-msvc": "npm:1.9.3" + "@swc/core-win32-x64-msvc": "npm:1.9.3" "@swc/counter": "npm:^0.1.3" - "@swc/types": "npm:^0.1.15" + "@swc/types": "npm:^0.1.17" peerDependencies: "@swc/helpers": "*" dependenciesMeta: @@ -3411,7 +3602,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 10c0/697e601fa1246367ca67e87e87c45f6341373ae98d8d24c9586c4069660c73f8675bf94b86cf218308395eda8e355ae076fc8c9c8f7aaa50898c228db38b637d + checksum: 10c0/a9507a5be580518d51cf7f41821a89e1044be6f72930efbdf3877366c27e9ff1dbca3e1a7f18698679f8c345b6698f43cd80d7dfa24ba30dcab493de9b7a336e languageName: node linkType: hard @@ -3422,12 +3613,12 @@ __metadata: languageName: node linkType: hard -"@swc/types@npm:^0.1.15": - version: 0.1.15 - resolution: "@swc/types@npm:0.1.15" +"@swc/types@npm:^0.1.17": + version: 0.1.17 + resolution: "@swc/types@npm:0.1.17" dependencies: "@swc/counter": "npm:^0.1.3" - checksum: 10c0/82bcfa64e53c6c93ae162fe9e491e5f300227fad6f110e32d9718e5a0e29586bc79c516234f6eccbe5ccd7ed72b514a21f03196a54408cf1b7b47c072fad44f0 + checksum: 10c0/29f5c8933a16042956f1adb7383e836ed7646cbf679826e78b53fdd0c08e8572cb42152e527b6b530a9bd1052d33d0972f90f589761ccd252c12652c9b7a72fc languageName: node linkType: hard @@ -3494,12 +3685,19 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*": - version: 22.9.0 - resolution: "@types/node@npm:22.9.0" +"@types/node@npm:*, @types/node@npm:^22.9.0, @types/node@npm:^22.9.1": + version: 22.10.0 + resolution: "@types/node@npm:22.10.0" dependencies: - undici-types: "npm:~6.19.8" - checksum: 10c0/3f46cbe0a49bab4ba30494025e4c8a6e699b98ac922857aa1f0209ce11a1313ee46e6808b8f13fe5b8b960a9d7796b77c8d542ad4e9810e85ef897d5593b5d51 + undici-types: "npm:~6.20.0" + checksum: 10c0/efb3783b6fe74b4300c5bdd4f245f1025887d9b1d0950edae584af58a30d95cc058c10b4b3428f8300e4318468b605240c2ede8fcfb6ead2e0f05bca31e54c1b + languageName: node + linkType: hard + +"@types/normalize-package-data@npm:^2.4.3": + version: 2.4.4 + resolution: "@types/normalize-package-data@npm:2.4.4" + checksum: 10c0/aef7bb9b015883d6f4119c423dd28c4bdc17b0e8a0ccf112c78b4fe0e91fbc4af7c6204b04bba0e199a57d2f3fbbd5b4a14bf8739bf9d2a39b2a0aad545e0f86 languageName: node linkType: hard @@ -3683,13 +3881,13 @@ __metadata: linkType: hard "@vitejs/plugin-react-swc@npm:^3.7.0": - version: 3.7.1 - resolution: "@vitejs/plugin-react-swc@npm:3.7.1" + version: 3.7.2 + resolution: "@vitejs/plugin-react-swc@npm:3.7.2" dependencies: "@swc/core": "npm:^1.7.26" peerDependencies: - vite: ^4 || ^5 - checksum: 10c0/2d613e69c0d0b809c94df80ca2b0caf39c50f0b98aa1f8599fd086bc37dac1449898eb6572000e1c133313137cac93440c4cb0861e05820c78bd2c07a52e64a8 + vite: ^4 || ^5 || ^6 + checksum: 10c0/9b9a5e0540791ba96a9fe4e8b8146ab274edcc730315535705f20126d6dfaffe72ae474bac9904ce841976e1959b6ecffd047bb2f0b7abf4d85aae7fbfdd00ab languageName: node linkType: hard @@ -3756,12 +3954,12 @@ __metadata: languageName: node linkType: hard -"@w3ux/extension-assets@npm:0.4.0, @w3ux/extension-assets@npm:^0.4.0": - version: 0.4.0 - resolution: "@w3ux/extension-assets@npm:0.4.0" +"@w3ux/extension-assets@npm:1.0.0-beta.1, @w3ux/extension-assets@npm:^1.0.0-beta.1": + version: 1.0.0-beta.1 + resolution: "@w3ux/extension-assets@npm:1.0.0-beta.1" peerDependencies: react: ^18 - checksum: 10c0/8cacff647d2d9c8deef43e83f57cfeb30a252cb716afb18c048b876012b9ce8f10e24d2a79f91e180014a46f9c9ca3346fd061fd6b9f98175881176bee90d4a2 + checksum: 10c0/bb9d43db351e333d7fa6b49977ef2ce7077bfe7d855dd0960f5e9e0e08d35bfe92b6431f35a74d3140b3fb4642296db7d96b188c80cce2e393c2c9c066244983 languageName: node linkType: hard @@ -3774,15 +3972,6 @@ __metadata: languageName: node linkType: hard -"@w3ux/hooks@npm:^1.1.0": - version: 1.2.1 - resolution: "@w3ux/hooks@npm:1.2.1" - peerDependencies: - react: ^18 - checksum: 10c0/542eae337e26c2202b53625ee7178bcf8fd748aac5b37c8851ef143251a09e37bf8a95916039661dcf76fe31004d3231c87d2dad6237251dc843351282173d72 - languageName: node - linkType: hard - "@w3ux/hooks@npm:^1.3.1-beta.7": version: 1.3.1-beta.7 resolution: "@w3ux/hooks@npm:1.3.1-beta.7" @@ -3795,18 +3984,17 @@ __metadata: languageName: node linkType: hard -"@w3ux/react-connect-kit@npm:^1.8.0": - version: 1.8.0 - resolution: "@w3ux/react-connect-kit@npm:1.8.0" +"@w3ux/react-connect-kit@npm:2.0.0-beta.2": + version: 2.0.0-beta.2 + resolution: "@w3ux/react-connect-kit@npm:2.0.0-beta.2" dependencies: - "@chainsafe/metamask-polkadot-adapter": "npm:^0.6.0" - "@polkagate/extension-dapp": "npm:^0.46.13" - "@w3ux/extension-assets": "npm:0.4.0" - "@w3ux/hooks": "npm:^1.1.0" - "@w3ux/utils": "npm:^0.9.0" + "@polkagate/extension-dapp": "npm:^0.48.2" + "@w3ux/extension-assets": "npm:1.0.0-beta.1" + "@w3ux/hooks": "npm:^1.3.1-beta.7" + "@w3ux/utils": "npm:^1.1.1-beta.11" peerDependencies: "@polkadot/api": ^11 - checksum: 10c0/8023634fdcd2a2fd50e6774f47d10ccc6c83c608821228372958f5bf21317830ce12a805c0306a5a6594c9b19375c1b2603602a1779f7e1fdf8579940b8fc633 + checksum: 10c0/8db87ebe1a5ff9eac3a275e05043c8f920283b75edd193393ee27898708673aa034fd4ed7a71a1ffc915e477f1c1a33bb5159d023b8a2fdaa164cc9a8f573113 languageName: node linkType: hard @@ -3841,18 +4029,7 @@ __metadata: languageName: node linkType: hard -"@w3ux/utils@npm:^0.9.0": - version: 0.9.1 - resolution: "@w3ux/utils@npm:0.9.1" - dependencies: - "@polkadot/util": "npm:^13.1.1" - "@polkadot/util-crypto": "npm:^13.1.1" - bignumber.js: "npm:^9.1.1" - checksum: 10c0/624ca3fd80cd5a0ed16b28342b41d643cc1aac7dbe68e00f3c4a9e460da21ac28d09e86582aad7c642e5907069c4544705c3bc638d836625916694e21774861c - languageName: node - linkType: hard - -"@w3ux/utils@npm:^1.1.1-beta.11": +"@w3ux/utils@npm:^1.1.1-beta.11, @w3ux/utils@npm:^1.1.1-beta.9": version: 1.1.1-beta.11 resolution: "@w3ux/utils@npm:1.1.1-beta.11" dependencies: @@ -3861,15 +4038,6 @@ __metadata: languageName: node linkType: hard -"@w3ux/utils@npm:^1.1.1-beta.9": - version: 1.1.1-beta.9 - resolution: "@w3ux/utils@npm:1.1.1-beta.9" - dependencies: - "@polkadot-api/substrate-bindings": "npm:^0.9.3" - checksum: 10c0/b18cf8274d43cf3419e8b47fc822164653da7a48a83a6eb84820b9fe14386e5f167ad1f634bfddac938f472839bc605e9bb04e8a88cfd12223b5b40b24730934 - languageName: node - linkType: hard - "@w3ux/validator-assets@npm:^0.2.0": version: 0.2.0 resolution: "@w3ux/validator-assets@npm:0.2.0" @@ -3879,9 +4047,9 @@ __metadata: languageName: node linkType: hard -"@wagmi/connectors@npm:5.3.10": - version: 5.3.10 - resolution: "@wagmi/connectors@npm:5.3.10" +"@wagmi/connectors@npm:5.5.0": + version: 5.5.0 + resolution: "@wagmi/connectors@npm:5.5.0" dependencies: "@coinbase/wallet-sdk": "npm:4.2.3" "@metamask/sdk": "npm:0.30.1" @@ -3890,19 +4058,19 @@ __metadata: "@walletconnect/ethereum-provider": "npm:2.17.0" cbw-sdk: "npm:@coinbase/wallet-sdk@3.9.3" peerDependencies: - "@wagmi/core": 2.14.6 + "@wagmi/core": 2.15.0 typescript: ">=5.0.4" viem: 2.x peerDependenciesMeta: typescript: optional: true - checksum: 10c0/683affe0fe0a319fa7e84d9eca6069a12fed288c9caea589ef6547af530111a18538546babc15719b023cbbf1f324ccc59c668163aed7cc21ab84bb906f84cca + checksum: 10c0/bbf2e74b3f35725a5ea410763b4d7cbe41dab55bffa55204cf42c20d25a3368480223f018497c8493137c0fe0df28810e94008da7c379ec859e544cb98c95553 languageName: node linkType: hard -"@wagmi/core@npm:2.14.6": - version: 2.14.6 - resolution: "@wagmi/core@npm:2.14.6" +"@wagmi/core@npm:2.15.0": + version: 2.15.0 + resolution: "@wagmi/core@npm:2.15.0" dependencies: eventemitter3: "npm:5.0.1" mipd: "npm:0.0.7" @@ -3916,7 +4084,7 @@ __metadata: optional: true typescript: optional: true - checksum: 10c0/bc79ba678f00da5e769526875698e9dc1464fc650f3db27ecf9865b78f0690b7006bb36b0ea0acf6deb9ea5d5a84d343fc8ec6efaa9e9a73868ddca9a8eb046e + checksum: 10c0/9f8889e6b58677287799f4faccf6910b57d5ea8bdb12a6a4faf930a6a8c45dc3cb641e4eba697011ad195601a33fe30aea1a700d6dbc853de5138e3268f1cfb4 languageName: node linkType: hard @@ -4264,15 +4432,6 @@ __metadata: languageName: node linkType: hard -"@wry/trie@npm:^0.4.3": - version: 0.4.3 - resolution: "@wry/trie@npm:0.4.3" - dependencies: - tslib: "npm:^2.3.0" - checksum: 10c0/1a14edba595b1967d0cf38208c2660b2952a8e8a649bb669b67907df48f602c7f2acbe16c1e1b115afa7d7effb9f1a4dbde38eef16ee92e7521a511262a53281 - languageName: node - linkType: hard - "@wry/trie@npm:^0.5.0": version: 0.5.0 resolution: "@wry/trie@npm:0.5.0" @@ -4437,6 +4596,13 @@ __metadata: languageName: node linkType: hard +"any-promise@npm:^1.0.0": + version: 1.3.0 + resolution: "any-promise@npm:1.3.0" + checksum: 10c0/60f0298ed34c74fef50daab88e8dab786036ed5a7fad02e012ab57e376e0a0b4b29e83b95ea9b5e7d89df762f5f25119b83e00706ecaccb22cfbacee98d74889 + languageName: node + linkType: hard + "anymatch@npm:^3.1.3, anymatch@npm:~3.1.2": version: 3.1.3 resolution: "anymatch@npm:3.1.3" @@ -4459,14 +4625,14 @@ __metadata: "@fortawesome/react-fontawesome": "npm:^0.2.2" "@ledgerhq/hw-transport-webhid": "npm:^6.29.2" "@polkadot-api/merkleize-metadata": "npm:^1.1.4" - "@polkadot/api": "npm:^14.3.1" - "@polkadot/rpc-provider": "npm:^14.3.1" + "@polkadot-api/signers-common": "npm:^0.1.1" + "@polkadot-api/substrate-bindings": "npm:^0.9.3" "@polkawatch/ddp-client": "npm:^2.0.20" "@substrate/connect": "npm:^2.0.1" - "@w3ux/extension-assets": "npm:^0.4.0" + "@w3ux/extension-assets": "npm:^1.0.0-beta.1" "@w3ux/factories": "npm:^1.0.0" "@w3ux/hooks": "npm:^1.3.1-beta.7" - "@w3ux/react-connect-kit": "npm:^1.8.0" + "@w3ux/react-connect-kit": "npm:2.0.0-beta.2" "@w3ux/react-odometer": "npm:^1.1.0" "@w3ux/react-polkicon": "npm:^2.0.1-alpha.0" "@w3ux/utils": "npm:^1.1.1-beta.11" @@ -4484,6 +4650,7 @@ __metadata: i18next-browser-languagedetector: "npm:^8.0.0" locales: "workspace:*" plugin-staking-api: "workspace:*" + polkadot-api: "npm:^1.7.3" qrcode-generator: "npm:1.4.4" rc-slider: "npm:^11.1.6" react: "npm:^18.3.1" @@ -4493,6 +4660,7 @@ __metadata: react-helmet: "npm:^6.1.0" react-router-dom: "npm:^6.23.1" react-scroll: "npm:^1.9.0" + rxjs: "npm:^7.8.1" styled-components: "npm:^6.1.13" styles: "workspace:*" ui-buttons: "workspace:*" @@ -4672,13 +4840,13 @@ __metadata: linkType: hard "axios@npm:^1.7.4": - version: 1.7.7 - resolution: "axios@npm:1.7.7" + version: 1.7.8 + resolution: "axios@npm:1.7.8" dependencies: follow-redirects: "npm:^1.15.6" form-data: "npm:^4.0.0" proxy-from-env: "npm:^1.1.0" - checksum: 10c0/4499efc89e86b0b49ffddc018798de05fab26e3bf57913818266be73279a6418c3ce8f9e934c7d2d707ab8c095e837fc6c90608fb7715b94d357720b5f568af7 + checksum: 10c0/23ae2d0105aea9170c34ac9b6f30d9b2ab2fa8b1370205d2f7ce98b9f9510ab420148c13359ee837ea5a4bf2fb028ff225bd2fc92052fb0c478c6b4a836e2d5f languageName: node linkType: hard @@ -4696,7 +4864,7 @@ __metadata: languageName: node linkType: hard -"bignumber.js@npm:^9.1.1, bignumber.js@npm:^9.1.2": +"bignumber.js@npm:^9.1.2": version: 9.1.2 resolution: "bignumber.js@npm:9.1.2" checksum: 10c0/e17786545433f3110b868725c449fa9625366a6e675cd70eb39b60938d6adbd0158cb4b3ad4f306ce817165d37e63f4aa3098ba4110db1d9a3b9f66abfbaf10d @@ -4818,6 +4986,17 @@ __metadata: languageName: node linkType: hard +"bundle-require@npm:^5.0.0": + version: 5.0.0 + resolution: "bundle-require@npm:5.0.0" + dependencies: + load-tsconfig: "npm:^0.2.3" + peerDependencies: + esbuild: ">=0.18" + checksum: 10c0/92c46df02586e0ebd66ee4831c9b5775adb3c32a43fe2b2aaf7bc675135c141f751de6a9a26b146d64c607c5b40f9eef5f10dce3c364f602d4bed268444c32c6 + languageName: node + linkType: hard + "cac@npm:^6.7.14": version: 6.7.14 resolution: "cac@npm:6.7.14" @@ -4887,9 +5066,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.30001669": - version: 1.0.30001680 - resolution: "caniuse-lite@npm:1.0.30001680" - checksum: 10c0/11a4e7f6f5d5f965cfd4b7dc4aef34e12a26e99647f02b5ac9fd7f7670845473b95ada416a785473237e4b1b67281f7b043c8736c85b77097f6b697e8950b15f + version: 1.0.30001684 + resolution: "caniuse-lite@npm:1.0.30001684" + checksum: 10c0/446485ca3d9caf408a339a44636a86a2b119ec247492393ae661cd93dccd6668401dd2dfec1e149be4e44563cd1e23351b44453a52fa2c2f19e2bf3287c865f6 languageName: node linkType: hard @@ -4935,6 +5114,13 @@ __metadata: languageName: node linkType: hard +"chalk@npm:^5.3.0": + version: 5.3.0 + resolution: "chalk@npm:5.3.0" + checksum: 10c0/8297d436b2c0f95801103ff2ef67268d362021b8210daf8ddbe349695333eb3610a71122172ff3b0272f1ef2cf7cc2c41fdaa4715f52e49ffe04c56340feed09 + languageName: node + linkType: hard + "chart.js@npm:^4.4.4": version: 4.4.6 resolution: "chart.js@npm:4.4.6" @@ -4972,6 +5158,15 @@ __metadata: languageName: node linkType: hard +"chokidar@npm:^4.0.1": + version: 4.0.1 + resolution: "chokidar@npm:4.0.1" + dependencies: + readdirp: "npm:^4.0.1" + checksum: 10c0/4bb7a3adc304059810bb6c420c43261a15bb44f610d77c35547addc84faa0374265c3adc67f25d06f363d9a4571962b02679268c40de07676d260de1986efea9 + languageName: node + linkType: hard + "chownr@npm:^2.0.0": version: 2.0.0 resolution: "chownr@npm:2.0.0" @@ -5009,6 +5204,22 @@ __metadata: languageName: node linkType: hard +"cli-cursor@npm:^5.0.0": + version: 5.0.0 + resolution: "cli-cursor@npm:5.0.0" + dependencies: + restore-cursor: "npm:^5.0.0" + checksum: 10c0/7ec62f69b79f6734ab209a3e4dbdc8af7422d44d360a7cb1efa8a0887bbe466a6e625650c466fe4359aee44dbe2dc0b6994b583d40a05d0808a5cb193641d220 + languageName: node + linkType: hard + +"cli-spinners@npm:^2.9.2": + version: 2.9.2 + resolution: "cli-spinners@npm:2.9.2" + checksum: 10c0/907a1c227ddf0d7a101e7ab8b300affc742ead4b4ebe920a5bf1bc6d45dce2958fcd195eb28fa25275062fe6fa9b109b93b63bc8033396ed3bcb50297008b3a3 + languageName: node + linkType: hard + "clipboardy@npm:^4.0.0": version: 4.0.0 resolution: "clipboardy@npm:4.0.0" @@ -5094,6 +5305,20 @@ __metadata: languageName: node linkType: hard +"commander@npm:^12.1.0": + version: 12.1.0 + resolution: "commander@npm:12.1.0" + checksum: 10c0/6e1996680c083b3b897bfc1cfe1c58dfbcd9842fd43e1aaf8a795fbc237f65efcc860a3ef457b318e73f29a4f4a28f6403c3d653d021d960e4632dd45bde54a9 + languageName: node + linkType: hard + +"commander@npm:^4.0.0": + version: 4.1.1 + resolution: "commander@npm:4.1.1" + checksum: 10c0/84a76c08fe6cc08c9c93f62ac573d2907d8e79138999312c92d4155bc2325d487d64d13f669b2000c9f8caf70493c1be2dac74fec3c51d5a04f8bc3ae1830bab + languageName: node + linkType: hard + "commander@npm:^8.0.0": version: 8.3.0 resolution: "commander@npm:8.3.0" @@ -5202,18 +5427,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.3": - version: 7.0.5 - resolution: "cross-spawn@npm:7.0.5" - dependencies: - path-key: "npm:^3.1.0" - shebang-command: "npm:^2.0.0" - which: "npm:^2.0.1" - checksum: 10c0/aa82ce7ac0814a27e6f2b738c5a7cf1fa21a3558a1e42df449fc96541ba3ba731e4d3ecffa4435348808a86212f287c6f20a1ee551ef1ff95d01cfec5f434944 - languageName: node - linkType: hard - -"cross-spawn@npm:^7.0.5": +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.5": version: 7.0.6 resolution: "cross-spawn@npm:7.0.6" dependencies: @@ -5314,7 +5528,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:~4.3.1, debug@npm:~4.3.2": +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.3.7, debug@npm:~4.3.1, debug@npm:~4.3.2": version: 4.3.7 resolution: "debug@npm:4.3.7" dependencies: @@ -5372,6 +5586,13 @@ __metadata: languageName: node linkType: hard +"deepmerge-ts@npm:^7.1.0": + version: 7.1.3 + resolution: "deepmerge-ts@npm:7.1.3" + checksum: 10c0/c9cfe7742a2c8f785302378b004381e1b831e3307ffe0c17be4b98fd87f347cb52a550aa9ff9ee0608b97f25400972ab79484f3836d77ec733828b10c8dcc522 + languageName: node + linkType: hard + "define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.4": version: 1.1.4 resolution: "define-data-property@npm:1.1.4" @@ -5429,6 +5650,13 @@ __metadata: languageName: node linkType: hard +"detect-indent@npm:^7.0.1": + version: 7.0.1 + resolution: "detect-indent@npm:7.0.1" + checksum: 10c0/47b6e3e3dda603c386e73b129f3e84844ae59bc2615f5072becf3cc02eab400bed5a4e6379c49d0b18cf630e80c2b07e87e0038b777addbc6ef793ad77dd05bc + languageName: node + linkType: hard + "detect-libc@npm:^1.0.3": version: 1.0.3 resolution: "detect-libc@npm:1.0.3" @@ -5498,21 +5726,21 @@ __metadata: linkType: hard "eciesjs@npm:^0.4.8": - version: 0.4.11 - resolution: "eciesjs@npm:0.4.11" + version: 0.4.12 + resolution: "eciesjs@npm:0.4.12" dependencies: "@ecies/ciphers": "npm:^0.2.1" "@noble/ciphers": "npm:^1.0.0" "@noble/curves": "npm:^1.6.0" "@noble/hashes": "npm:^1.5.0" - checksum: 10c0/3b707a46313d6a7629b37e6f86bb72b038f3acef994b270e5683644278bdc2278a4b125beb6a010ba2a3944c2ada469a7d3e87ff7cbd50b808f191c03530ea54 + checksum: 10c0/c2fc1afc9cfe79e89c13f82d7b9441d503b7cacf99b6cd221d75acf78678fde401608b7c49fd54965b5a83c6d36d04b510b78c3dc1499d8bc8cfcaea84666aab languageName: node linkType: hard "electron-to-chromium@npm:^1.5.41": - version: 1.5.57 - resolution: "electron-to-chromium@npm:1.5.57" - checksum: 10c0/42b969681985016be6069ae68cf29e84ba3f2191fcb7f9d3355e83e81da8dbd100e4b5c9d69b88637003e06dc1860125a50332ec0caee49fd9c2c4ab62feb288 + version: 1.5.65 + resolution: "electron-to-chromium@npm:1.5.65" + checksum: 10c0/4d2db76ca63d34aad9d5392d850a89fecb4d740a3f0e3ab945f23850ed99789df4e09dd36a28cedcf3b4757dd7c82d5d159bfdf1d29f815d172a9132b4ba3bb9 languageName: node linkType: hard @@ -5531,6 +5759,13 @@ __metadata: languageName: node linkType: hard +"emoji-regex@npm:^10.3.0": + version: 10.4.0 + resolution: "emoji-regex@npm:10.4.0" + checksum: 10c0/a3fcedfc58bfcce21a05a5f36a529d81e88d602100145fcca3dc6f795e3c8acc4fc18fe773fbf9b6d6e9371205edb3afa2668ec3473fa2aa7fd47d2a9d46482d + languageName: node + linkType: hard + "emoji-regex@npm:^8.0.0": version: 8.0.0 resolution: "emoji-regex@npm:8.0.0" @@ -5639,9 +5874,9 @@ __metadata: languageName: node linkType: hard -"es-abstract@npm:^1.17.5, es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3, es-abstract@npm:^1.23.0, es-abstract@npm:^1.23.1, es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.3": - version: 1.23.4 - resolution: "es-abstract@npm:1.23.4" +"es-abstract@npm:^1.17.5, es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3, es-abstract@npm:^1.23.0, es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.3, es-abstract@npm:^1.23.5": + version: 1.23.5 + resolution: "es-abstract@npm:1.23.5" dependencies: array-buffer-byte-length: "npm:^1.0.1" arraybuffer.prototype.slice: "npm:^1.0.3" @@ -5689,7 +5924,7 @@ __metadata: typed-array-length: "npm:^1.0.6" unbox-primitive: "npm:^1.0.2" which-typed-array: "npm:^1.1.15" - checksum: 10c0/70c56ec479d57e63387f561bb50c80587f4a52010868787e3d4b4f95301edf5ba98d70ffd0ba56eb4722c48c578870ff2a8825236a948cfa483f76015d134acb + checksum: 10c0/1f6f91da9cf7ee2c81652d57d3046621d598654d1d1b05c1578bafe5c4c2d3d69513901679bdca2de589f620666ec21de337e4935cec108a4ed0871d5ef04a5d languageName: node linkType: hard @@ -5852,6 +6087,89 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:^0.24.0": + version: 0.24.0 + resolution: "esbuild@npm:0.24.0" + dependencies: + "@esbuild/aix-ppc64": "npm:0.24.0" + "@esbuild/android-arm": "npm:0.24.0" + "@esbuild/android-arm64": "npm:0.24.0" + "@esbuild/android-x64": "npm:0.24.0" + "@esbuild/darwin-arm64": "npm:0.24.0" + "@esbuild/darwin-x64": "npm:0.24.0" + "@esbuild/freebsd-arm64": "npm:0.24.0" + "@esbuild/freebsd-x64": "npm:0.24.0" + "@esbuild/linux-arm": "npm:0.24.0" + "@esbuild/linux-arm64": "npm:0.24.0" + "@esbuild/linux-ia32": "npm:0.24.0" + "@esbuild/linux-loong64": "npm:0.24.0" + "@esbuild/linux-mips64el": "npm:0.24.0" + "@esbuild/linux-ppc64": "npm:0.24.0" + "@esbuild/linux-riscv64": "npm:0.24.0" + "@esbuild/linux-s390x": "npm:0.24.0" + "@esbuild/linux-x64": "npm:0.24.0" + "@esbuild/netbsd-x64": "npm:0.24.0" + "@esbuild/openbsd-arm64": "npm:0.24.0" + "@esbuild/openbsd-x64": "npm:0.24.0" + "@esbuild/sunos-x64": "npm:0.24.0" + "@esbuild/win32-arm64": "npm:0.24.0" + "@esbuild/win32-ia32": "npm:0.24.0" + "@esbuild/win32-x64": "npm:0.24.0" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/9f1aadd8d64f3bff422ae78387e66e51a5e09de6935a6f987b6e4e189ed00fdc2d1bc03d2e33633b094008529c8b6e06c7ad1a9782fb09fec223bf95998c0683 + languageName: node + linkType: hard + "escalade@npm:^3.1.1, escalade@npm:^3.2.0": version: 3.2.0 resolution: "escalade@npm:3.2.0" @@ -6274,6 +6592,26 @@ __metadata: languageName: node linkType: hard +"execa@npm:^9.5.1": + version: 9.5.1 + resolution: "execa@npm:9.5.1" + dependencies: + "@sindresorhus/merge-streams": "npm:^4.0.0" + cross-spawn: "npm:^7.0.3" + figures: "npm:^6.1.0" + get-stream: "npm:^9.0.0" + human-signals: "npm:^8.0.0" + is-plain-obj: "npm:^4.1.0" + is-stream: "npm:^4.0.1" + npm-run-path: "npm:^6.0.0" + pretty-ms: "npm:^9.0.0" + signal-exit: "npm:^4.1.0" + strip-final-newline: "npm:^4.0.0" + yoctocolors: "npm:^2.0.0" + checksum: 10c0/1a628d535c5a088f9e17a735bb3143efc4198095392b319ba877b2975d5c3c57724536dccb6f68f1cd9b3af331c5a9e8c1aeb338d52ab316b1e008ff453374a7 + languageName: node + linkType: hard + "exponential-backoff@npm:^3.1.1": version: 3.1.1 resolution: "exponential-backoff@npm:3.1.1" @@ -6355,6 +6693,18 @@ __metadata: languageName: node linkType: hard +"fdir@npm:^6.4.2": + version: 6.4.2 + resolution: "fdir@npm:6.4.2" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10c0/34829886f34a3ca4170eca7c7180ec4de51a3abb4d380344063c0ae2e289b11d2ba8b724afee974598c83027fea363ff598caf2b51bc4e6b1e0d8b80cc530573 + languageName: node + linkType: hard + "fetch-blob@npm:^3.1.2, fetch-blob@npm:^3.1.4": version: 3.2.0 resolution: "fetch-blob@npm:3.2.0" @@ -6372,6 +6722,15 @@ __metadata: languageName: node linkType: hard +"figures@npm:^6.1.0": + version: 6.1.0 + resolution: "figures@npm:6.1.0" + dependencies: + is-unicode-supported: "npm:^2.0.0" + checksum: 10c0/9159df4264d62ef447a3931537de92f5012210cf5135c35c010df50a2169377581378149abfe1eb238bd6acbba1c0d547b1f18e0af6eee49e30363cedaffcfe4 + languageName: node + linkType: hard + "file-entry-cache@npm:^8.0.0": version: 8.0.0 resolution: "file-entry-cache@npm:8.0.0" @@ -6428,9 +6787,9 @@ __metadata: linkType: hard "flatted@npm:^3.2.9": - version: 3.3.1 - resolution: "flatted@npm:3.3.1" - checksum: 10c0/324166b125ee07d4ca9bcf3a5f98d915d5db4f39d711fba640a3178b959919aae1f7cfd8aabcfef5826ed8aa8a2aa14cc85b2d7d18ff638ddf4ae3df39573eaf + version: 3.3.2 + resolution: "flatted@npm:3.3.2" + checksum: 10c0/24cc735e74d593b6c767fe04f2ef369abe15b62f6906158079b9874bdb3ee5ae7110bb75042e70cd3f99d409d766f357caf78d5ecee9780206f5fdc5edbad334 languageName: node linkType: hard @@ -6484,8 +6843,8 @@ __metadata: linkType: hard "framer-motion@npm:^11.11.1": - version: 11.11.15 - resolution: "framer-motion@npm:11.11.15" + version: 11.11.17 + resolution: "framer-motion@npm:11.11.17" dependencies: tslib: "npm:^2.4.0" peerDependencies: @@ -6499,7 +6858,7 @@ __metadata: optional: true react-dom: optional: true - checksum: 10c0/515d1f758ebf8c7257f80586fe459acbb77f323e7a2f3e8b20bf53023960f5e54b1cf364b39fa2ded2ec0558c66e78c681b3903cfac11fefdb51efcfaff21443 + checksum: 10c0/4ec88a568e636c5409a238668770a7d0237270b4b58fdf27d0cab1f9094a4fdd22d25d827a1bba51e52f01e9d2db65544b32a5feb8eff9a63c8798f2e64677d0 languageName: node linkType: hard @@ -6532,6 +6891,13 @@ __metadata: languageName: node linkType: hard +"fs.promises.exists@npm:^1.1.4": + version: 1.1.4 + resolution: "fs.promises.exists@npm:1.1.4" + checksum: 10c0/6e200a97e869c8b84b4dabc5c963d87d20db8704fa12098773b472a61651c0a822b651807ff883e09b8480c41f5184acb65abdd9965b950b3cb2b473b10c3c0f + languageName: node + linkType: hard + "fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": version: 2.3.3 resolution: "fsevents@npm:2.3.3" @@ -6591,6 +6957,13 @@ __metadata: languageName: node linkType: hard +"get-east-asian-width@npm:^1.0.0": + version: 1.3.0 + resolution: "get-east-asian-width@npm:1.3.0" + checksum: 10c0/1a049ba697e0f9a4d5514c4623781c5246982bdb61082da6b5ae6c33d838e52ce6726407df285cdbb27ec1908b333cf2820989bd3e986e37bb20979437fdf34b + languageName: node + linkType: hard + "get-func-name@npm:^2.0.1, get-func-name@npm:^2.0.2": version: 2.0.2 resolution: "get-func-name@npm:2.0.2" @@ -6625,6 +6998,16 @@ __metadata: languageName: node linkType: hard +"get-stream@npm:^9.0.0": + version: 9.0.1 + resolution: "get-stream@npm:9.0.1" + dependencies: + "@sec-ant/readable-stream": "npm:^0.4.1" + is-stream: "npm:^4.0.1" + checksum: 10c0/d70e73857f2eea1826ac570c3a912757dcfbe8a718a033fa0c23e12ac8e7d633195b01710e0559af574cbb5af101009b42df7b6f6b29ceec8dbdf7291931b948 + languageName: node + linkType: hard + "get-symbol-description@npm:^1.0.2": version: 1.0.2 resolution: "get-symbol-description@npm:1.0.2" @@ -6700,7 +7083,7 @@ __metadata: languageName: node linkType: hard -"globalthis@npm:^1.0.3, globalthis@npm:^1.0.4": +"globalthis@npm:^1.0.4": version: 1.0.4 resolution: "globalthis@npm:1.0.4" dependencies: @@ -6868,6 +7251,15 @@ __metadata: languageName: node linkType: hard +"hosted-git-info@npm:^7.0.0": + version: 7.0.2 + resolution: "hosted-git-info@npm:7.0.2" + dependencies: + lru-cache: "npm:^10.0.1" + checksum: 10c0/b19dbd92d3c0b4b0f1513cf79b0fc189f54d6af2129eeb201de2e9baaa711f1936929c848b866d9c8667a0f956f34bf4f07418c12be1ee9ca74fd9246335ca1f + languageName: node + linkType: hard + "howler@npm:^2.2.3": version: 2.2.4 resolution: "howler@npm:2.2.4" @@ -6932,6 +7324,13 @@ __metadata: languageName: node linkType: hard +"human-signals@npm:^8.0.0": + version: 8.0.0 + resolution: "human-signals@npm:8.0.0" + checksum: 10c0/e4dac4f7d3eb791ed04129fc6a85bd454a9102d3e3b76c911d0db7057ebd60b2956b435b5b5712aec18960488ede3c21ef7c56e42cdd70760c0d84d3c05cd92e + languageName: node + linkType: hard + "i18next-browser-languagedetector@npm:7.1.0": version: 7.1.0 resolution: "i18next-browser-languagedetector@npm:7.1.0" @@ -6960,8 +7359,8 @@ __metadata: linkType: hard "i18next@npm:^24.0.0": - version: 24.0.0 - resolution: "i18next@npm:24.0.0" + version: 24.0.2 + resolution: "i18next@npm:24.0.2" dependencies: "@babel/runtime": "npm:^7.23.2" peerDependencies: @@ -6969,7 +7368,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/675e87241883dffb18f7bea6d996475777f14406e55c2915ecafb6e4ec28c3e921b675f194e99c3de0f9f234a4a2e204f8c53cfc4fd34a1db89f98c191713034 + checksum: 10c0/6bd2d9a8566026e1a8f97e6d797c323007ad816bf6e90f13cf0114d67908a845b88a5b9701d3d0dabffd38785a6ac145ea9d54265d8ef0f872931c7fa9579e16 languageName: node linkType: hard @@ -7051,6 +7450,13 @@ __metadata: languageName: node linkType: hard +"index-to-position@npm:^0.1.2": + version: 0.1.2 + resolution: "index-to-position@npm:0.1.2" + checksum: 10c0/7c91bde8bafc22684b74a7a24915bee4691cba48352ddb4ebe3b20a3a87bc0fa7a05f586137245ca8f92222a11f341f7631ff7f38cd78a523505d2d02dbfa257 + languageName: node + linkType: hard + "inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3": version: 2.0.4 resolution: "inherits@npm:2.0.4" @@ -7234,12 +7640,12 @@ __metadata: languageName: node linkType: hard -"is-finalizationregistry@npm:^1.0.2": - version: 1.0.2 - resolution: "is-finalizationregistry@npm:1.0.2" +"is-finalizationregistry@npm:^1.1.0": + version: 1.1.0 + resolution: "is-finalizationregistry@npm:1.1.0" dependencies: - call-bind: "npm:^1.0.2" - checksum: 10c0/81caecc984d27b1a35c68741156fc651fb1fa5e3e6710d21410abc527eb226d400c0943a167922b2e920f6b3e58b0dede9aa795882b038b85f50b3a4b877db86 + call-bind: "npm:^1.0.7" + checksum: 10c0/1cd94236bfb6e060fe2b973c8726a2782727f7d495b3e8e1d51d3e619c5a3345413706f555956eb5b12af15eba0414118f64a1b19d793ec36b5e6767a13836ac languageName: node linkType: hard @@ -7279,6 +7685,13 @@ __metadata: languageName: node linkType: hard +"is-interactive@npm:^2.0.0": + version: 2.0.0 + resolution: "is-interactive@npm:2.0.0" + checksum: 10c0/801c8f6064f85199dc6bf99b5dd98db3282e930c3bc197b32f2c5b89313bb578a07d1b8a01365c4348c2927229234f3681eb861b9c2c92bee72ff397390fa600 + languageName: node + linkType: hard + "is-lambda@npm:^1.0.1": version: 1.0.1 resolution: "is-lambda@npm:1.0.1" @@ -7316,6 +7729,13 @@ __metadata: languageName: node linkType: hard +"is-plain-obj@npm:^4.0.0, is-plain-obj@npm:^4.1.0": + version: 4.1.0 + resolution: "is-plain-obj@npm:4.1.0" + checksum: 10c0/32130d651d71d9564dc88ba7e6fda0e91a1010a3694648e9f4f47bb6080438140696d3e3e15c741411d712e47ac9edc1a8a9de1fe76f3487b0d90be06ac9975e + languageName: node + linkType: hard + "is-regex@npm:^1.1.4": version: 1.1.4 resolution: "is-regex@npm:1.1.4" @@ -7356,6 +7776,13 @@ __metadata: languageName: node linkType: hard +"is-stream@npm:^4.0.1": + version: 4.0.1 + resolution: "is-stream@npm:4.0.1" + checksum: 10c0/2706c7f19b851327ba374687bc4a3940805e14ca496dc672b9629e744d143b1ad9c6f1b162dece81c7bfbc0f83b32b61ccc19ad2e05aad2dd7af347408f60c7f + languageName: node + linkType: hard + "is-string@npm:^1.0.5, is-string@npm:^1.0.7": version: 1.0.7 resolution: "is-string@npm:1.0.7" @@ -7383,6 +7810,20 @@ __metadata: languageName: node linkType: hard +"is-unicode-supported@npm:^1.3.0": + version: 1.3.0 + resolution: "is-unicode-supported@npm:1.3.0" + checksum: 10c0/b8674ea95d869f6faabddc6a484767207058b91aea0250803cbf1221345cb0c56f466d4ecea375dc77f6633d248d33c47bd296fb8f4cdba0b4edba8917e83d8a + languageName: node + linkType: hard + +"is-unicode-supported@npm:^2.0.0": + version: 2.1.0 + resolution: "is-unicode-supported@npm:2.1.0" + checksum: 10c0/a0f53e9a7c1fdbcf2d2ef6e40d4736fdffff1c9f8944c75e15425118ff3610172c87bf7bc6c34d3903b04be59790bb2212ddbe21ee65b5a97030fc50370545a5 + languageName: node + linkType: hard + "is-weakmap@npm:^2.0.2": version: 2.0.2 resolution: "is-weakmap@npm:2.0.2" @@ -7508,6 +7949,13 @@ __metadata: languageName: node linkType: hard +"joycon@npm:^3.1.1": + version: 3.1.1 + resolution: "joycon@npm:3.1.1" + checksum: 10c0/131fb1e98c9065d067fd49b6e685487ac4ad4d254191d7aa2c9e3b90f4e9ca70430c43cad001602bdbdabcf58717d3b5c5b7461c1bd8e39478c8de706b3fe6ae + languageName: node + linkType: hard + "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -7515,10 +7963,10 @@ __metadata: languageName: node linkType: hard -"js-tokens@npm:^9.0.0": - version: 9.0.0 - resolution: "js-tokens@npm:9.0.0" - checksum: 10c0/4ad1c12f47b8c8b2a3a99e29ef338c1385c7b7442198a425f3463f3537384dab6032012791bfc2f056ea5ecdb06b1ed4f70e11a3ab3f388d3dcebfe16a52b27d +"js-tokens@npm:^9.0.1": + version: 9.0.1 + resolution: "js-tokens@npm:9.0.1" + checksum: 10c0/68dcab8f233dde211a6b5fd98079783cbcd04b53617c1250e3553ee16ab3e6134f5e65478e41d82f6d351a052a63d71024553933808570f04dbf828d7921e80e languageName: node linkType: hard @@ -7684,6 +8132,13 @@ __metadata: languageName: node linkType: hard +"lilconfig@npm:^3.1.1": + version: 3.1.2 + resolution: "lilconfig@npm:3.1.2" + checksum: 10c0/f059630b1a9bddaeba83059db00c672b64dc14074e9f232adce32b38ca1b5686ab737eb665c5ba3c32f147f0002b4bee7311ad0386a9b98547b5623e87071fbe + languageName: node + linkType: hard + "lines-and-columns@npm:^1.1.6": version: 1.2.4 resolution: "lines-and-columns@npm:1.2.4" @@ -7751,13 +8206,20 @@ __metadata: languageName: node linkType: hard +"load-tsconfig@npm:^0.2.3": + version: 0.2.5 + resolution: "load-tsconfig@npm:0.2.5" + checksum: 10c0/bf2823dd26389d3497b6567f07435c5a7a58d9df82e879b0b3892f87d8db26900f84c85bc329ef41c0540c0d6a448d1c23ddc64a80f3ff6838b940f3915a3fcb + languageName: node + linkType: hard + "local-pkg@npm:^0.5.0": - version: 0.5.0 - resolution: "local-pkg@npm:0.5.0" + version: 0.5.1 + resolution: "local-pkg@npm:0.5.1" dependencies: - mlly: "npm:^1.4.2" - pkg-types: "npm:^1.0.3" - checksum: 10c0/f61cbd00d7689f275558b1a45c7ff2a3ddf8472654123ed880215677b9adfa729f1081e50c27ffb415cdb9fa706fb755fec5e23cdd965be375c8059e87ff1cc9 + mlly: "npm:^1.7.3" + pkg-types: "npm:^1.2.1" + checksum: 10c0/ade8346f1dc04875921461adee3c40774b00d4b74095261222ebd4d5fd0a444676e36e325f76760f21af6a60bc82480e154909b54d2d9f7173671e36dacf1808 languageName: node linkType: hard @@ -7806,6 +8268,13 @@ __metadata: languageName: node linkType: hard +"lodash.sortby@npm:^4.7.0": + version: 4.7.0 + resolution: "lodash.sortby@npm:4.7.0" + checksum: 10c0/fc48fb54ff7669f33bb32997cab9460757ee99fafaf72400b261c3e10fde21538e47d8cfcbe6a25a31bcb5b7b727c27d52626386fc2de24eb059a6d64a89cdf5 + languageName: node + linkType: hard + "lodash.throttle@npm:^4.1.1": version: 4.1.1 resolution: "lodash.throttle@npm:4.1.1" @@ -7813,6 +8282,16 @@ __metadata: languageName: node linkType: hard +"log-symbols@npm:^6.0.0": + version: 6.0.0 + resolution: "log-symbols@npm:6.0.0" + dependencies: + chalk: "npm:^5.3.0" + is-unicode-supported: "npm:^1.3.0" + checksum: 10c0/36636cacedba8f067d2deb4aad44e91a89d9efb3ead27e1846e7b82c9a10ea2e3a7bd6ce28a7ca616bebc60954ff25c67b0f92d20a6a746bb3cc52c3701891f6 + languageName: node + linkType: hard + "loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" @@ -7866,11 +8345,11 @@ __metadata: linkType: hard "magic-string@npm:^0.30.5": - version: 0.30.12 - resolution: "magic-string@npm:0.30.12" + version: 0.30.14 + resolution: "magic-string@npm:0.30.14" dependencies: "@jridgewell/sourcemap-codec": "npm:^1.5.0" - checksum: 10c0/469f457d18af37dfcca8617086ea8a65bcd8b60ba8a1182cb024ce43e470ace3c9d1cb6bee58d3b311768fb16bc27bd50bdeebcaa63dadd0fd46cac4d2e11d5f + checksum: 10c0/c52c2a6e699dfa8a840e13154da35464a40cd8b07049b695a8b282883b0426c0811af1e36ac26860b4267289340b42772c156a5608e87be97b63d510e617e87a languageName: node linkType: hard @@ -7957,6 +8436,13 @@ __metadata: languageName: node linkType: hard +"mimic-function@npm:^5.0.0": + version: 5.0.1 + resolution: "mimic-function@npm:5.0.1" + checksum: 10c0/f3d9464dd1816ecf6bdf2aec6ba32c0728022039d992f178237d8e289b48764fee4131319e72eedd4f7f094e22ded0af836c3187a7edc4595d28dd74368fd81d + languageName: node + linkType: hard + "minimalistic-assert@npm:^1.0.0, minimalistic-assert@npm:^1.0.1": version: 1.0.1 resolution: "minimalistic-assert@npm:1.0.1" @@ -8101,7 +8587,7 @@ __metadata: languageName: node linkType: hard -"mlly@npm:^1.4.2, mlly@npm:^1.7.1, mlly@npm:^1.7.2": +"mlly@npm:^1.7.1, mlly@npm:^1.7.2, mlly@npm:^1.7.3": version: 1.7.3 resolution: "mlly@npm:1.7.3" dependencies: @@ -8148,12 +8634,23 @@ __metadata: languageName: node linkType: hard +"mz@npm:^2.7.0": + version: 2.7.0 + resolution: "mz@npm:2.7.0" + dependencies: + any-promise: "npm:^1.0.0" + object-assign: "npm:^4.0.1" + thenify-all: "npm:^1.0.0" + checksum: 10c0/103114e93f87362f0b56ab5b2e7245051ad0276b646e3902c98397d18bb8f4a77f2ea4a2c9d3ad516034ea3a56553b60d3f5f78220001ca4c404bd711bd0af39 + languageName: node + linkType: hard + "nanoid@npm:^3.3.7": - version: 3.3.7 - resolution: "nanoid@npm:3.3.7" + version: 3.3.8 + resolution: "nanoid@npm:3.3.8" bin: nanoid: bin/nanoid.cjs - checksum: 10c0/e3fb661aa083454f40500473bb69eedb85dc160e763150b9a2c567c7e9ff560ce028a9f833123b618a6ea742e311138b591910e795614a629029e86e180660f3 + checksum: 10c0/4b1bb29f6cfebf3be3bc4ad1f1296fb0a10a3043a79f34fbffe75d1621b4318319211cd420549459018ea3592f0d2f159247a6f874911d6d26eaaadda2478120 languageName: node linkType: hard @@ -8188,7 +8685,7 @@ __metadata: languageName: node linkType: hard -"nock@npm:^13.5.0, nock@npm:^13.5.5": +"nock@npm:^13.5.4": version: 13.5.6 resolution: "nock@npm:13.5.6" dependencies: @@ -8264,13 +8761,13 @@ __metadata: linkType: hard "node-gyp-build@npm:^4.2.0, node-gyp-build@npm:^4.3.0": - version: 4.8.3 - resolution: "node-gyp-build@npm:4.8.3" + version: 4.8.4 + resolution: "node-gyp-build@npm:4.8.4" bin: node-gyp-build: bin.js node-gyp-build-optional: optional.js node-gyp-build-test: build-test.js - checksum: 10c0/a7f43c4128d817db80bb0884f631121449ac586b4a3e708eab0db6fcb7fa0d2e66f6d7d4ee1f49469409de4a9b2e413926befe2dce70b850c6c583a3bbe228d2 + checksum: 10c0/444e189907ece2081fe60e75368784f7782cfddb554b60123743dfb89509df89f1f29c03bbfa16b3a3e0be3f48799a4783f487da6203245fa5bed239ba7407e1 languageName: node linkType: hard @@ -8312,6 +8809,17 @@ __metadata: languageName: node linkType: hard +"normalize-package-data@npm:^6.0.0": + version: 6.0.2 + resolution: "normalize-package-data@npm:6.0.2" + dependencies: + hosted-git-info: "npm:^7.0.0" + semver: "npm:^7.3.5" + validate-npm-package-license: "npm:^3.0.4" + checksum: 10c0/7e32174e7f5575ede6d3d449593247183880122b4967d4ae6edb28cea5769ca025defda54fc91ec0e3c972fdb5ab11f9284606ba278826171b264cb16a9311ef + languageName: node + linkType: hard + "normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0": version: 3.0.0 resolution: "normalize-path@npm:3.0.0" @@ -8337,6 +8845,16 @@ __metadata: languageName: node linkType: hard +"npm-run-path@npm:^6.0.0": + version: 6.0.0 + resolution: "npm-run-path@npm:6.0.0" + dependencies: + path-key: "npm:^4.0.0" + unicorn-magic: "npm:^0.3.0" + checksum: 10c0/b223c8a0dcd608abf95363ea5c3c0ccc3cd877daf0102eaf1b0f2390d6858d8337fbb7c443af2403b067a7d2c116d10691ecd22ab3c5273c44da1ff8d07753bd + languageName: node + linkType: hard + "obj-multiplex@npm:^1.0.0": version: 1.0.0 resolution: "obj-multiplex@npm:1.0.0" @@ -8348,7 +8866,7 @@ __metadata: languageName: node linkType: hard -"object-assign@npm:^4.1.1": +"object-assign@npm:^4.0.1, object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" checksum: 10c0/1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414 @@ -8469,6 +8987,15 @@ __metadata: languageName: node linkType: hard +"onetime@npm:^7.0.0": + version: 7.0.0 + resolution: "onetime@npm:7.0.0" + dependencies: + mimic-function: "npm:^5.0.0" + checksum: 10c0/5cb9179d74b63f52a196a2e7037ba2b9a893245a5532d3f44360012005c9cadb60851d56716ebff18a6f47129dab7168022445df47c2aff3b276d92585ed1221 + languageName: node + linkType: hard + "open@npm:^8.4.0": version: 8.4.2 resolution: "open@npm:8.4.2" @@ -8481,14 +9008,14 @@ __metadata: linkType: hard "optimism@npm:^0.18.0": - version: 0.18.0 - resolution: "optimism@npm:0.18.0" + version: 0.18.1 + resolution: "optimism@npm:0.18.1" dependencies: "@wry/caches": "npm:^1.0.0" "@wry/context": "npm:^0.7.0" - "@wry/trie": "npm:^0.4.3" + "@wry/trie": "npm:^0.5.0" tslib: "npm:^2.3.0" - checksum: 10c0/8e97c6d660cb80cf5f444209b9dd29ee6951fa7b344d4c4fc6d4aaf0ad0710dddaf834d0f5d7211b3658b15ef6c6a22cbcb98c7a8121e3fee9666fe0fd62d876 + checksum: 10c0/1c1cd065d306de2220c6a2bdd8701cb7f9aadace36a9f16d6e02db2bee23b0291f15a1219b92cde5c66d816bd33dca876dfdcdbad04b4cf9b2a7fc5a1a221e77 languageName: node linkType: hard @@ -8506,6 +9033,23 @@ __metadata: languageName: node linkType: hard +"ora@npm:^8.1.1": + version: 8.1.1 + resolution: "ora@npm:8.1.1" + dependencies: + chalk: "npm:^5.3.0" + cli-cursor: "npm:^5.0.0" + cli-spinners: "npm:^2.9.2" + is-interactive: "npm:^2.0.0" + is-unicode-supported: "npm:^2.0.0" + log-symbols: "npm:^6.0.0" + stdin-discarder: "npm:^0.2.2" + string-width: "npm:^7.2.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/996a81a9e997481339de3a7996c56131ea292c0a0e9e42d1cd454e2390f1ce7015ec925dcdd29e3d74dc5d037a4aa1877e575b491555507bcd9f219df760a63f + languageName: node + linkType: hard + "ox@npm:0.1.2": version: 0.1.2 resolution: "ox@npm:0.1.2" @@ -8622,6 +9166,24 @@ __metadata: languageName: node linkType: hard +"parse-json@npm:^8.0.0": + version: 8.1.0 + resolution: "parse-json@npm:8.1.0" + dependencies: + "@babel/code-frame": "npm:^7.22.13" + index-to-position: "npm:^0.1.2" + type-fest: "npm:^4.7.1" + checksum: 10c0/39a49acafc1c41a763df2599a826eb77873a44b098a5f2ba548843229b334a16ff9d613d0381328e58031b0afaabc18ed2a01337a6522911ac7a81828df58bcb + languageName: node + linkType: hard + +"parse-ms@npm:^4.0.0": + version: 4.0.0 + resolution: "parse-ms@npm:4.0.0" + checksum: 10c0/a7900f4f1ebac24cbf5e9708c16fb2fd482517fad353aecd7aefb8c2ba2f85ce017913ccb8925d231770404780df46244ea6fec598b3bde6490882358b4d2d16 + languageName: node + linkType: hard + "path-exists@npm:^4.0.0": version: 4.0.0 resolution: "path-exists@npm:4.0.0" @@ -8754,7 +9316,14 @@ __metadata: languageName: node linkType: hard -"pkg-types@npm:^1.0.3, pkg-types@npm:^1.2.1": +"pirates@npm:^4.0.1": + version: 4.0.6 + resolution: "pirates@npm:4.0.6" + checksum: 10c0/00d5fa51f8dded94d7429700fb91a0c1ead00ae2c7fd27089f0c5b63e6eca36197fe46384631872690a66f390c5e27198e99006ab77ae472692ab9c2ca903f36 + languageName: node + linkType: hard + +"pkg-types@npm:^1.2.1": version: 1.2.1 resolution: "pkg-types@npm:1.2.1" dependencies: @@ -8781,6 +9350,37 @@ __metadata: languageName: node linkType: hard +"polkadot-api@npm:^1.7.3": + version: 1.7.7 + resolution: "polkadot-api@npm:1.7.7" + dependencies: + "@polkadot-api/cli": "npm:0.9.21" + "@polkadot-api/ink-contracts": "npm:0.2.2" + "@polkadot-api/json-rpc-provider": "npm:0.0.4" + "@polkadot-api/known-chains": "npm:0.5.8" + "@polkadot-api/logs-provider": "npm:0.0.6" + "@polkadot-api/metadata-builders": "npm:0.9.2" + "@polkadot-api/metadata-compatibility": "npm:0.1.12" + "@polkadot-api/observable-client": "npm:0.6.3" + "@polkadot-api/pjs-signer": "npm:0.6.1" + "@polkadot-api/polkadot-sdk-compat": "npm:2.3.1" + "@polkadot-api/polkadot-signer": "npm:0.1.6" + "@polkadot-api/signer": "npm:0.1.11" + "@polkadot-api/sm-provider": "npm:0.1.7" + "@polkadot-api/smoldot": "npm:0.3.7" + "@polkadot-api/substrate-bindings": "npm:0.9.4" + "@polkadot-api/substrate-client": "npm:0.3.0" + "@polkadot-api/utils": "npm:0.1.2" + "@polkadot-api/ws-provider": "npm:0.3.6" + peerDependencies: + rxjs: ">=7.8.0" + bin: + papi: bin/cli.mjs + polkadot-api: bin/cli.mjs + checksum: 10c0/c1241d233662b78d9422bb97579bd75f667d15ee1ecc46cb4ef49041b5881d52084c08c97fbb59fee6a3a259a74d61250e0bbe64bb9eb3a1f511bc9fab662b6b + languageName: node + linkType: hard + "polkadot-staking-dashboard@workspace:.": version: 0.0.0-use.local resolution: "polkadot-staking-dashboard@workspace:." @@ -8832,6 +9432,29 @@ __metadata: languageName: node linkType: hard +"postcss-load-config@npm:^6.0.1": + version: 6.0.1 + resolution: "postcss-load-config@npm:6.0.1" + dependencies: + lilconfig: "npm:^3.1.1" + peerDependencies: + jiti: ">=1.21.0" + postcss: ">=8.0.9" + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + checksum: 10c0/74173a58816dac84e44853f7afbd283f4ef13ca0b6baeba27701214beec33f9e309b128f8102e2b173e8d45ecba45d279a9be94b46bf48d219626aa9b5730848 + languageName: node + linkType: hard + "postcss-value-parser@npm:^4.0.2": version: 4.2.0 resolution: "postcss-value-parser@npm:4.2.0" @@ -8862,9 +9485,9 @@ __metadata: linkType: hard "preact@npm:^10.16.0, preact@npm:^10.24.2": - version: 10.24.3 - resolution: "preact@npm:10.24.3" - checksum: 10c0/c863df6d7be6a660480189762d8a8f2d4148733fc2bb9efbd9d2fd27315d2c7ede850a16077d716c91666c915c0349bd3c9699733e4f08457226a0519f408761 + version: 10.25.0 + resolution: "preact@npm:10.25.0" + checksum: 10c0/703e021e7a268aea929e9c896484608e18dd9f5aa62c71d3205fedf69269d9592e4cfe17adda78ca4cec1f5f0057a062300daeeb477333ba2005a4d9e3fbc970 languageName: node linkType: hard @@ -8927,6 +9550,15 @@ __metadata: languageName: node linkType: hard +"pretty-ms@npm:^9.0.0": + version: 9.2.0 + resolution: "pretty-ms@npm:9.2.0" + dependencies: + parse-ms: "npm:^4.0.0" + checksum: 10c0/ab6d066f90e9f77020426986e1b018369f41575674544c539aabec2e63a20fec01166d8cf6571d0e165ad11cfe5a8134a2a48a36d42ab291c59c6deca5264cbb + languageName: node + linkType: hard + "proc-log@npm:^4.1.0, proc-log@npm:^4.2.0": version: 4.2.0 resolution: "proc-log@npm:4.2.0" @@ -9168,8 +9800,8 @@ __metadata: linkType: hard "react-i18next@npm:^15.1.1": - version: 15.1.1 - resolution: "react-i18next@npm:15.1.1" + version: 15.1.2 + resolution: "react-i18next@npm:15.1.2" dependencies: "@babel/runtime": "npm:^7.25.0" html-parse-stringify: "npm:^3.0.1" @@ -9181,7 +9813,7 @@ __metadata: optional: true react-native: optional: true - checksum: 10c0/c1e8af21422cdaa1a493b1d73502a496aab84c3318de1137bb56b1f2f37eae4ba5856cf079b0a0abe9f2a18e0457c986ad2e43e70875f7cd43d0efa2f70897a5 + checksum: 10c0/b10052545f0bebcd0b55cf04eb7d58307cdf6645707108bcae28026fe92ce0ef9cf0b35ac9cb940f6fcc22f679c810f2c008e9d8284bd4cfbb26c5649e3885ab languageName: node linkType: hard @@ -9267,6 +9899,19 @@ __metadata: languageName: node linkType: hard +"read-pkg@npm:^9.0.1": + version: 9.0.1 + resolution: "read-pkg@npm:9.0.1" + dependencies: + "@types/normalize-package-data": "npm:^2.4.3" + normalize-package-data: "npm:^6.0.0" + parse-json: "npm:^8.0.0" + type-fest: "npm:^4.6.0" + unicorn-magic: "npm:^0.1.0" + checksum: 10c0/f3e27549dcdb18335597f4125a3d093a40ab0a18c16a6929a1575360ed5d8679b709b4a672730d9abf6aa8537a7f02bae0b4b38626f99409255acbd8f72f9964 + languageName: node + linkType: hard + "readable-stream@npm:^2.3.3": version: 2.3.8 resolution: "readable-stream@npm:2.3.8" @@ -9306,6 +9951,13 @@ __metadata: languageName: node linkType: hard +"readdirp@npm:^4.0.1": + version: 4.0.2 + resolution: "readdirp@npm:4.0.2" + checksum: 10c0/a16ecd8ef3286dcd90648c3b103e3826db2b766cdb4a988752c43a83f683d01c7059158d623cbcd8bdfb39e65d302d285be2d208e7d9f34d022d912b929217dd + languageName: node + linkType: hard + "readdirp@npm:~3.6.0": version: 3.6.0 resolution: "readdirp@npm:3.6.0" @@ -9322,18 +9974,18 @@ __metadata: languageName: node linkType: hard -"reflect.getprototypeof@npm:^1.0.4": - version: 1.0.6 - resolution: "reflect.getprototypeof@npm:1.0.6" +"reflect.getprototypeof@npm:^1.0.4, reflect.getprototypeof@npm:^1.0.6": + version: 1.0.7 + resolution: "reflect.getprototypeof@npm:1.0.7" dependencies: call-bind: "npm:^1.0.7" define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.23.1" + es-abstract: "npm:^1.23.5" es-errors: "npm:^1.3.0" get-intrinsic: "npm:^1.2.4" - globalthis: "npm:^1.0.3" - which-builtin-type: "npm:^1.1.3" - checksum: 10c0/baf4ef8ee6ff341600f4720b251cf5a6cb552d6a6ab0fdc036988c451bf16f920e5feb0d46bd4f530a5cce568f1f7aca2d77447ca798920749cfc52783c39b55 + gopd: "npm:^1.0.1" + which-builtin-type: "npm:^1.1.4" + checksum: 10c0/841814f7631b55ee42e198cb14a5c25c0752431ab8f0ad9794c32d46ab9fb0d5ba4939edac1f99a174a21443a1400a72bccbbb9ccd9277e4b4bf6d14aabb31c8 languageName: node linkType: hard @@ -9392,6 +10044,13 @@ __metadata: languageName: node linkType: hard +"resolve-from@npm:^5.0.0": + version: 5.0.0 + resolution: "resolve-from@npm:5.0.0" + checksum: 10c0/b21cb7f1fb746de8107b9febab60095187781137fd803e6a59a76d421444b1531b641bba5857f5dc011974d8a5c635d61cec49e6bd3b7fc20e01f0fafc4efbf2 + languageName: node + linkType: hard + "resolve-pkg-maps@npm:^1.0.0": version: 1.0.0 resolution: "resolve-pkg-maps@npm:1.0.0" @@ -9458,6 +10117,16 @@ __metadata: languageName: node linkType: hard +"restore-cursor@npm:^5.0.0": + version: 5.1.0 + resolution: "restore-cursor@npm:5.1.0" + dependencies: + onetime: "npm:^7.0.0" + signal-exit: "npm:^4.1.0" + checksum: 10c0/c2ba89131eea791d1b25205bdfdc86699767e2b88dee2a590b1a6caa51737deac8bad0260a5ded2f7c074b7db2f3a626bcf1fcf3cdf35974cbeea5e2e6764f60 + languageName: node + linkType: hard + "retry@npm:^0.12.0": version: 0.12.0 resolution: "retry@npm:0.12.0" @@ -9505,28 +10174,28 @@ __metadata: languageName: node linkType: hard -"rollup@npm:^4.20.0": - version: 4.26.0 - resolution: "rollup@npm:4.26.0" - dependencies: - "@rollup/rollup-android-arm-eabi": "npm:4.26.0" - "@rollup/rollup-android-arm64": "npm:4.26.0" - "@rollup/rollup-darwin-arm64": "npm:4.26.0" - "@rollup/rollup-darwin-x64": "npm:4.26.0" - "@rollup/rollup-freebsd-arm64": "npm:4.26.0" - "@rollup/rollup-freebsd-x64": "npm:4.26.0" - "@rollup/rollup-linux-arm-gnueabihf": "npm:4.26.0" - "@rollup/rollup-linux-arm-musleabihf": "npm:4.26.0" - "@rollup/rollup-linux-arm64-gnu": "npm:4.26.0" - "@rollup/rollup-linux-arm64-musl": "npm:4.26.0" - "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.26.0" - "@rollup/rollup-linux-riscv64-gnu": "npm:4.26.0" - "@rollup/rollup-linux-s390x-gnu": "npm:4.26.0" - "@rollup/rollup-linux-x64-gnu": "npm:4.26.0" - "@rollup/rollup-linux-x64-musl": "npm:4.26.0" - "@rollup/rollup-win32-arm64-msvc": "npm:4.26.0" - "@rollup/rollup-win32-ia32-msvc": "npm:4.26.0" - "@rollup/rollup-win32-x64-msvc": "npm:4.26.0" +"rollup@npm:^4.20.0, rollup@npm:^4.24.0": + version: 4.27.4 + resolution: "rollup@npm:4.27.4" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.27.4" + "@rollup/rollup-android-arm64": "npm:4.27.4" + "@rollup/rollup-darwin-arm64": "npm:4.27.4" + "@rollup/rollup-darwin-x64": "npm:4.27.4" + "@rollup/rollup-freebsd-arm64": "npm:4.27.4" + "@rollup/rollup-freebsd-x64": "npm:4.27.4" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.27.4" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.27.4" + "@rollup/rollup-linux-arm64-gnu": "npm:4.27.4" + "@rollup/rollup-linux-arm64-musl": "npm:4.27.4" + "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.27.4" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.27.4" + "@rollup/rollup-linux-s390x-gnu": "npm:4.27.4" + "@rollup/rollup-linux-x64-gnu": "npm:4.27.4" + "@rollup/rollup-linux-x64-musl": "npm:4.27.4" + "@rollup/rollup-win32-arm64-msvc": "npm:4.27.4" + "@rollup/rollup-win32-ia32-msvc": "npm:4.27.4" + "@rollup/rollup-win32-x64-msvc": "npm:4.27.4" "@types/estree": "npm:1.0.6" fsevents: "npm:~2.3.2" dependenciesMeta: @@ -9570,7 +10239,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: 10c0/a4375787b95bc3b55d38bbb8dec5f6a63862b08369b9562a2d38efadd400ca42a79406b8f09670a0deb0cc9cd72cca1c0be317302190d1f7feff597003d951bc + checksum: 10c0/1442650cfea5e4617ce14743784f6f578817e31db56f9c8aaf96a82daa9bc20b6ccd66c0d677dbf302a4da3e70664dc3bef11a1aec85e6aff3cecccb945b1d35 languageName: node linkType: hard @@ -9878,30 +10547,30 @@ __metadata: languageName: node linkType: hard -"smoldot@npm:2.0.22": - version: 2.0.22 - resolution: "smoldot@npm:2.0.22" +"smoldot@npm:2.0.26": + version: 2.0.26 + resolution: "smoldot@npm:2.0.26" dependencies: ws: "npm:^8.8.1" - checksum: 10c0/fa439bebfe0d0d46e4da342a313d0653fd56557b6459b5ba3db1fd6bcfb345e9d9577c27e1f6692e67dec0206addb95de6b594c17041729f5689b4b123974495 + checksum: 10c0/a4788fb92e5ed6e8c3d171d00474712c6f98f62cae68543f1029e7976a64ce9c8126956e50d6bd89482df8568f8ac043d5eb50b63f44f9a6062cbc49f0ef2dad languageName: node linkType: hard -"smoldot@npm:2.0.26": - version: 2.0.26 - resolution: "smoldot@npm:2.0.26" +"smoldot@npm:2.0.33": + version: 2.0.33 + resolution: "smoldot@npm:2.0.33" dependencies: ws: "npm:^8.8.1" - checksum: 10c0/a4788fb92e5ed6e8c3d171d00474712c6f98f62cae68543f1029e7976a64ce9c8126956e50d6bd89482df8568f8ac043d5eb50b63f44f9a6062cbc49f0ef2dad + checksum: 10c0/dada517972924017077b4e8c6f76169ddcd371c270e714db42e034c0c974898a15f640759576b712f9d0aa3e2f1bb01c7e5d2e711e329f0f6837902ac51bb1fa languageName: node linkType: hard -"smoldot@npm:2.0.30": - version: 2.0.30 - resolution: "smoldot@npm:2.0.30" +"smoldot@npm:^2.0.34": + version: 2.0.34 + resolution: "smoldot@npm:2.0.34" dependencies: ws: "npm:^8.8.1" - checksum: 10c0/d4aaa7169f2933d3cbcc78720d22a8dee7d038c01f5868d1ccc022e5ebb9deab33ee7fe235f933ae863eccccc65735313c4e2645864730f8a421d44dbd11398b + checksum: 10c0/b9079a70706c54ee16d0ec0b626f6f13807b6130856c570b84ca8dd0506283110017bcc7696d08f3a15e70c9bf2d5ba8a2d7249665347185593193beb1907d1e languageName: node linkType: hard @@ -9967,6 +10636,15 @@ __metadata: languageName: node linkType: hard +"sort-keys@npm:^5.0.0": + version: 5.1.0 + resolution: "sort-keys@npm:5.1.0" + dependencies: + is-plain-obj: "npm:^4.0.0" + checksum: 10c0/fdb7aeb02368ad91b2ea947b59f3c95d80f8c71bbcb5741ebd55852994f54a129af3b3663b280951566fe5897de056428810dbb58c61db831e588c0ac110f2b0 + languageName: node + linkType: hard + "source-map-js@npm:>=0.6.2 <2.0.0, source-map-js@npm:^1.2.0, source-map-js@npm:^1.2.1": version: 1.2.1 resolution: "source-map-js@npm:1.2.1" @@ -9981,6 +10659,15 @@ __metadata: languageName: node linkType: hard +"source-map@npm:0.8.0-beta.0": + version: 0.8.0-beta.0 + resolution: "source-map@npm:0.8.0-beta.0" + dependencies: + whatwg-url: "npm:^7.0.0" + checksum: 10c0/fb4d9bde9a9fdb2c29b10e5eae6c71d10e09ef467e1afb75fdec2eb7e11fa5b343a2af553f74f18b695dbc0b81f9da2e9fa3d7a317d5985e9939499ec6087835 + languageName: node + linkType: hard + "source-map@npm:^0.7.4": version: 0.7.4 resolution: "source-map@npm:0.7.4" @@ -9988,6 +10675,40 @@ __metadata: languageName: node linkType: hard +"spdx-correct@npm:^3.0.0": + version: 3.2.0 + resolution: "spdx-correct@npm:3.2.0" + dependencies: + spdx-expression-parse: "npm:^3.0.0" + spdx-license-ids: "npm:^3.0.0" + checksum: 10c0/49208f008618b9119208b0dadc9208a3a55053f4fd6a0ae8116861bd22696fc50f4142a35ebfdb389e05ccf2de8ad142573fefc9e26f670522d899f7b2fe7386 + languageName: node + linkType: hard + +"spdx-exceptions@npm:^2.1.0": + version: 2.5.0 + resolution: "spdx-exceptions@npm:2.5.0" + checksum: 10c0/37217b7762ee0ea0d8b7d0c29fd48b7e4dfb94096b109d6255b589c561f57da93bf4e328c0290046115961b9209a8051ad9f525e48d433082fc79f496a4ea940 + languageName: node + linkType: hard + +"spdx-expression-parse@npm:^3.0.0": + version: 3.0.1 + resolution: "spdx-expression-parse@npm:3.0.1" + dependencies: + spdx-exceptions: "npm:^2.1.0" + spdx-license-ids: "npm:^3.0.0" + checksum: 10c0/6f8a41c87759fa184a58713b86c6a8b028250f158159f1d03ed9d1b6ee4d9eefdc74181c8ddc581a341aa971c3e7b79e30b59c23b05d2436d5de1c30bdef7171 + languageName: node + linkType: hard + +"spdx-license-ids@npm:^3.0.0": + version: 3.0.20 + resolution: "spdx-license-ids@npm:3.0.20" + checksum: 10c0/bdff7534fad6ef59be49becda1edc3fb7f5b3d6f296a715516ab9d972b8ad59af2c34b2003e01db8970d4c673d185ff696ba74c6b61d3bf327e2b3eac22c297c + languageName: node + linkType: hard + "split-on-first@npm:^1.0.0": version: 1.1.0 resolution: "split-on-first@npm:1.1.0" @@ -10069,6 +10790,13 @@ __metadata: languageName: node linkType: hard +"stdin-discarder@npm:^0.2.2": + version: 0.2.2 + resolution: "stdin-discarder@npm:0.2.2" + checksum: 10c0/c78375e82e956d7a64be6e63c809c7f058f5303efcaf62ea48350af072bacdb99c06cba39209b45a071c1acbd49116af30df1df9abb448df78a6005b72f10537 + languageName: node + linkType: hard + "stream-shift@npm:^1.0.2": version: 1.0.3 resolution: "stream-shift@npm:1.0.3" @@ -10105,6 +10833,17 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^7.2.0": + version: 7.2.0 + resolution: "string-width@npm:7.2.0" + dependencies: + emoji-regex: "npm:^10.3.0" + get-east-asian-width: "npm:^1.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/eb0430dd43f3199c7a46dcbf7a0b34539c76fe3aa62763d0b0655acdcbdf360b3f66f3d58ca25ba0205f42ea3491fa00f09426d3b7d3040e506878fc7664c9b9 + languageName: node + linkType: hard + "string.prototype.matchall@npm:^4.0.11": version: 4.0.11 resolution: "string.prototype.matchall@npm:4.0.11" @@ -10196,7 +10935,7 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^7.0.1": +"strip-ansi@npm:^7.0.1, strip-ansi@npm:^7.1.0": version: 7.1.0 resolution: "strip-ansi@npm:7.1.0" dependencies: @@ -10219,6 +10958,13 @@ __metadata: languageName: node linkType: hard +"strip-final-newline@npm:^4.0.0": + version: 4.0.0 + resolution: "strip-final-newline@npm:4.0.0" + checksum: 10c0/b0cf2b62d597a1b0e3ebc42b88767f0a0d45601f89fd379a928a1812c8779440c81abba708082c946445af1d6b62d5f16e2a7cf4f30d9d6587b89425fae801ff + languageName: node + linkType: hard + "strip-json-comments@npm:^3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" @@ -10227,11 +10973,11 @@ __metadata: linkType: hard "strip-literal@npm:^2.0.0": - version: 2.1.0 - resolution: "strip-literal@npm:2.1.0" + version: 2.1.1 + resolution: "strip-literal@npm:2.1.1" dependencies: - js-tokens: "npm:^9.0.0" - checksum: 10c0/bc8b8c8346125ae3c20fcdaf12e10a498ff85baf6f69597b4ab2b5fbf2e58cfd2827f1a44f83606b852da99a5f6c8279770046ddea974c510c17c98934c9cc24 + js-tokens: "npm:^9.0.1" + checksum: 10c0/66a7353f5ba1ae6a4fb2805b4aba228171847200640083117c41512692e6b2c020e18580402984f55c0ae69c30f857f9a55abd672863e4ca8fdb463fdf93ba19 languageName: node linkType: hard @@ -10270,6 +11016,24 @@ __metadata: languageName: node linkType: hard +"sucrase@npm:^3.35.0": + version: 3.35.0 + resolution: "sucrase@npm:3.35.0" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.2" + commander: "npm:^4.0.0" + glob: "npm:^10.3.10" + lines-and-columns: "npm:^1.1.6" + mz: "npm:^2.7.0" + pirates: "npm:^4.0.1" + ts-interface-checker: "npm:^0.1.9" + bin: + sucrase: bin/sucrase + sucrase-node: bin/sucrase-node + checksum: 10c0/ac85f3359d2c2ecbf5febca6a24ae9bf96c931f05fde533c22a94f59c6a74895e5d5f0e871878dfd59c2697a75ebb04e4b2224ef0bfc24ca1210735c2ec191ef + languageName: node + linkType: hard + "superstruct@npm:^1.0.3": version: 1.0.4 resolution: "superstruct@npm:1.0.4" @@ -10345,6 +11109,24 @@ __metadata: languageName: node linkType: hard +"thenify-all@npm:^1.0.0": + version: 1.6.0 + resolution: "thenify-all@npm:1.6.0" + dependencies: + thenify: "npm:>= 3.1.0 < 4" + checksum: 10c0/9b896a22735e8122754fe70f1d65f7ee691c1d70b1f116fda04fea103d0f9b356e3676cb789506e3909ae0486a79a476e4914b0f92472c2e093d206aed4b7d6b + languageName: node + linkType: hard + +"thenify@npm:>= 3.1.0 < 4": + version: 3.3.1 + resolution: "thenify@npm:3.3.1" + dependencies: + any-promise: "npm:^1.0.0" + checksum: 10c0/f375aeb2b05c100a456a30bc3ed07ef03a39cbdefe02e0403fb714b8c7e57eeaad1a2f5c4ecfb9ce554ce3db9c2b024eba144843cd9e344566d9fcee73b04767 + languageName: node + linkType: hard + "thread-stream@npm:^0.15.1": version: 0.15.2 resolution: "thread-stream@npm:0.15.2" @@ -10368,6 +11150,23 @@ __metadata: languageName: node linkType: hard +"tinyexec@npm:^0.3.1": + version: 0.3.1 + resolution: "tinyexec@npm:0.3.1" + checksum: 10c0/11e7a7c5d8b3bddf8b5cbe82a9290d70a6fad84d528421d5d18297f165723cb53d2e737d8f58dcce5ca56f2e4aa2d060f02510b1f8971784f97eb3e9aec28f09 + languageName: node + linkType: hard + +"tinyglobby@npm:^0.2.9": + version: 0.2.10 + resolution: "tinyglobby@npm:0.2.10" + dependencies: + fdir: "npm:^6.4.2" + picomatch: "npm:^4.0.2" + checksum: 10c0/ce946135d39b8c0e394e488ad59f4092e8c4ecd675ef1bcd4585c47de1b325e61ec6adfbfbe20c3c2bfa6fd674c5b06de2a2e65c433f752ae170aff11793e5ef + languageName: node + linkType: hard + "tinypool@npm:^0.8.3": version: 0.8.4 resolution: "tinypool@npm:0.8.4" @@ -10398,6 +11197,15 @@ __metadata: languageName: node linkType: hard +"tr46@npm:^1.0.1": + version: 1.0.1 + resolution: "tr46@npm:1.0.1" + dependencies: + punycode: "npm:^2.1.0" + checksum: 10c0/41525c2ccce86e3ef30af6fa5e1464e6d8bb4286a58ea8db09228f598889581ef62347153f6636cd41553dc41685bdfad0a9d032ef58df9fbb0792b3447d0f04 + languageName: node + linkType: hard + "tr46@npm:~0.0.3": version: 0.0.3 resolution: "tr46@npm:0.0.3" @@ -10405,12 +11213,28 @@ __metadata: languageName: node linkType: hard +"tree-kill@npm:^1.2.2": + version: 1.2.2 + resolution: "tree-kill@npm:1.2.2" + bin: + tree-kill: cli.js + checksum: 10c0/7b1b7c7f17608a8f8d20a162e7957ac1ef6cd1636db1aba92f4e072dc31818c2ff0efac1e3d91064ede67ed5dc57c565420531a8134090a12ac10cf792ab14d2 + languageName: node + linkType: hard + "ts-api-utils@npm:^1.3.0": - version: 1.4.0 - resolution: "ts-api-utils@npm:1.4.0" + version: 1.4.2 + resolution: "ts-api-utils@npm:1.4.2" peerDependencies: typescript: ">=4.2.0" - checksum: 10c0/1b2bfa50ea52771d564bb143bb69010d25cda03ed573095fbac9b86f717012426443af6647e00e3db70fca60360482a30c1be7cf73c3521c321f6bf5e3594ea0 + checksum: 10c0/b9d82922af42cefa14650397f5ff42a1ff8c8a1b4fac3590fa3e2daeeb3666fbe260a324f55dc748d9653dce30c2a21a148fba928511b2022bedda66423695bf + languageName: node + linkType: hard + +"ts-interface-checker@npm:^0.1.9": + version: 0.1.13 + resolution: "ts-interface-checker@npm:0.1.13" + checksum: 10c0/232509f1b84192d07b81d1e9b9677088e590ac1303436da1e92b296e9be8e31ea042e3e1fd3d29b1742ad2c959e95afe30f63117b8f1bc3a3850070a5142fea7 languageName: node linkType: hard @@ -10423,6 +11247,15 @@ __metadata: languageName: node linkType: hard +"tsc-prog@npm:^2.3.0": + version: 2.3.0 + resolution: "tsc-prog@npm:2.3.0" + peerDependencies: + typescript: ">=4" + checksum: 10c0/ca0ee722557e7974a221d6b3fa28dcbcc5e98b7bce9402bf113eae7c835d59644d24b48ac65d15c7f8dbe8cab61c54c4b0b2d252212c72bc4d09ce1fe8fbc937 + languageName: node + linkType: hard + "tsconfck@npm:^3.0.3": version: 3.1.4 resolution: "tsconfck@npm:3.1.4" @@ -10463,13 +11296,54 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.6.2, tslib@npm:^2.7.0, tslib@npm:^2.8.0": +"tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.6.2, tslib@npm:^2.6.3, tslib@npm:^2.7.0, tslib@npm:^2.8.0": version: 2.8.1 resolution: "tslib@npm:2.8.1" checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 languageName: node linkType: hard +"tsup@npm:^8.3.5": + version: 8.3.5 + resolution: "tsup@npm:8.3.5" + dependencies: + bundle-require: "npm:^5.0.0" + cac: "npm:^6.7.14" + chokidar: "npm:^4.0.1" + consola: "npm:^3.2.3" + debug: "npm:^4.3.7" + esbuild: "npm:^0.24.0" + joycon: "npm:^3.1.1" + picocolors: "npm:^1.1.1" + postcss-load-config: "npm:^6.0.1" + resolve-from: "npm:^5.0.0" + rollup: "npm:^4.24.0" + source-map: "npm:0.8.0-beta.0" + sucrase: "npm:^3.35.0" + tinyexec: "npm:^0.3.1" + tinyglobby: "npm:^0.2.9" + tree-kill: "npm:^1.2.2" + peerDependencies: + "@microsoft/api-extractor": ^7.36.0 + "@swc/core": ^1 + postcss: ^8.4.12 + typescript: ">=4.5.0" + peerDependenciesMeta: + "@microsoft/api-extractor": + optional: true + "@swc/core": + optional: true + postcss: + optional: true + typescript: + optional: true + bin: + tsup: dist/cli-default.js + tsup-node: dist/cli-node.js + checksum: 10c0/7794953cbc784b7c8f14c4898d36a293b815b528d3098c2416aeaa2b4775dc477132cd12f75f6d32737dfd15ba10139c73f7039045352f2ba1ea7e5fe6fe3773 + languageName: node + linkType: hard + "type-check@npm:^0.4.0, type-check@npm:~0.4.0": version: 0.4.0 resolution: "type-check@npm:0.4.0" @@ -10493,6 +11367,13 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:^4.23.0, type-fest@npm:^4.6.0, type-fest@npm:^4.7.1": + version: 4.28.1 + resolution: "type-fest@npm:4.28.1" + checksum: 10c0/8b0dabe2f063dff6dff1964d70f1d844ea13a7534e9aa454b48569d5837f76252bb4518bbd3ca98b4fb65e49a732c555649cf60fc02b0254a0d69eb00c518725 + languageName: node + linkType: hard + "typed-array-buffer@npm:^1.0.2": version: 1.0.2 resolution: "typed-array-buffer@npm:1.0.2" @@ -10518,8 +11399,8 @@ __metadata: linkType: hard "typed-array-byte-offset@npm:^1.0.2": - version: 1.0.2 - resolution: "typed-array-byte-offset@npm:1.0.2" + version: 1.0.3 + resolution: "typed-array-byte-offset@npm:1.0.3" dependencies: available-typed-arrays: "npm:^1.0.7" call-bind: "npm:^1.0.7" @@ -10527,21 +11408,22 @@ __metadata: gopd: "npm:^1.0.1" has-proto: "npm:^1.0.3" is-typed-array: "npm:^1.1.13" - checksum: 10c0/d2628bc739732072e39269389a758025f75339de2ed40c4f91357023c5512d237f255b633e3106c461ced41907c1bf9a533c7e8578066b0163690ca8bc61b22f + reflect.getprototypeof: "npm:^1.0.6" + checksum: 10c0/5da29585f96671c0521475226d3227000b3e01d1e99208b66bb05b75c7c8f4d0e9cc2e79920f3bfbc792a00102df1daa2608a2753e3f291b671d5a80245bde5b languageName: node linkType: hard "typed-array-length@npm:^1.0.6": - version: 1.0.6 - resolution: "typed-array-length@npm:1.0.6" + version: 1.0.7 + resolution: "typed-array-length@npm:1.0.7" dependencies: call-bind: "npm:^1.0.7" for-each: "npm:^0.3.3" gopd: "npm:^1.0.1" - has-proto: "npm:^1.0.3" is-typed-array: "npm:^1.1.13" possible-typed-array-names: "npm:^1.0.0" - checksum: 10c0/74253d7dc488eb28b6b2711cf31f5a9dcefc9c41b0681fd1c178ed0a1681b4468581a3626d39cd4df7aee3d3927ab62be06aa9ca74e5baf81827f61641445b77 + reflect.getprototypeof: "npm:^1.0.6" + checksum: 10c0/e38f2ae3779584c138a2d8adfa8ecf749f494af3cd3cdafe4e688ce51418c7d2c5c88df1bd6be2bbea099c3f7cea58c02ca02ed438119e91f162a9de23f61295 languageName: node linkType: hard @@ -10561,7 +11443,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.7.2": +"typescript@npm:^5.6.3, typescript@npm:^5.7.2": version: 5.7.2 resolution: "typescript@npm:5.7.2" bin: @@ -10571,7 +11453,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.7.2#optional!builtin": +"typescript@patch:typescript@npm%3A^5.6.3#optional!builtin, typescript@patch:typescript@npm%3A^5.7.2#optional!builtin": version: 5.7.2 resolution: "typescript@patch:typescript@npm%3A5.7.2#optional!builtin::version=5.7.2&hash=5adc0c" bin: @@ -10645,10 +11527,10 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~6.19.8": - version: 6.19.8 - resolution: "undici-types@npm:6.19.8" - checksum: 10c0/078afa5990fba110f6824823ace86073b4638f1d5112ee26e790155f481f2a868cc3e0615505b6f4282bdf74a3d8caad715fd809e870c2bb0704e3ea6082f344 +"undici-types@npm:~6.20.0": + version: 6.20.0 + resolution: "undici-types@npm:6.20.0" + checksum: 10c0/68e659a98898d6a836a9a59e6adf14a5d799707f5ea629433e025ac90d239f75e408e2e5ff086afc3cace26f8b26ee52155293564593fbb4a2f666af57fc59bf languageName: node linkType: hard @@ -10665,6 +11547,20 @@ __metadata: languageName: node linkType: hard +"unicorn-magic@npm:^0.1.0": + version: 0.1.0 + resolution: "unicorn-magic@npm:0.1.0" + checksum: 10c0/e4ed0de05b0a05e735c7d8a2930881e5efcfc3ec897204d5d33e7e6247f4c31eac92e383a15d9a6bccb7319b4271ee4bea946e211bf14951fec6ff2cbbb66a92 + languageName: node + linkType: hard + +"unicorn-magic@npm:^0.3.0": + version: 0.3.0 + resolution: "unicorn-magic@npm:0.3.0" + checksum: 10c0/0a32a997d6c15f1c2a077a15b1c4ca6f268d574cf5b8975e778bb98e6f8db4ef4e86dfcae4e158cd4c7e38fb4dd383b93b13eefddc7f178dea13d3ac8a603271 + languageName: node + linkType: hard + "unique-filename@npm:^3.0.0": version: 3.0.0 resolution: "unique-filename@npm:3.0.0" @@ -10867,6 +11763,16 @@ __metadata: languageName: node linkType: hard +"validate-npm-package-license@npm:^3.0.4": + version: 3.0.4 + resolution: "validate-npm-package-license@npm:3.0.4" + dependencies: + spdx-correct: "npm:^3.0.0" + spdx-expression-parse: "npm:^3.0.0" + checksum: 10c0/7b91e455a8de9a0beaa9fe961e536b677da7f48c9a493edf4d4d4a87fd80a7a10267d438723364e432c2fcd00b5650b5378275cded362383ef570276e6312f4f + languageName: node + linkType: hard + "valtio@npm:1.11.2": version: 1.11.2 resolution: "valtio@npm:1.11.2" @@ -10886,8 +11792,8 @@ __metadata: linkType: hard "viem@npm:^2.1.1, viem@npm:^2.21.35": - version: 2.21.45 - resolution: "viem@npm:2.21.45" + version: 2.21.51 + resolution: "viem@npm:2.21.51" dependencies: "@noble/curves": "npm:1.6.0" "@noble/hashes": "npm:1.5.0" @@ -10903,7 +11809,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/3ed93c58dc125c97f35970b17d3dca6dfe6a8811d45fed84dc5bdd4a57860c880ae5a41a45cb8caa500925450b52d3a225d8def8422e7dd7ecead3bcc3921ec8 + checksum: 10c0/aca0e6ad3826ce16b7c8463f84063d382769b2fb21d5d1ef3794866e5939e7a73d0e108e0cc45e1d830449d18b935ae1f7d14821adf529586700e066a771cf65 languageName: node linkType: hard @@ -11016,8 +11922,8 @@ __metadata: linkType: hard "vite-tsconfig-paths@npm:^5.0.1": - version: 5.1.2 - resolution: "vite-tsconfig-paths@npm:5.1.2" + version: 5.1.3 + resolution: "vite-tsconfig-paths@npm:5.1.3" dependencies: debug: "npm:^4.1.1" globrex: "npm:^0.1.2" @@ -11027,7 +11933,7 @@ __metadata: peerDependenciesMeta: vite: optional: true - checksum: 10c0/7db445b6b1f48e7b89f39f5eb8cf4ea645994f581fcc7c9fac721e0c36f8203c0770007ec27825caa6e2566e3127b2b1bfe8be28ca05cd0e9fb67a2943dcdec5 + checksum: 10c0/fb7480efa31fd50439f4a12c91bc953e5cc09d69fdc7eeb6ffff7cc796bc2c1f2c617c3abfdcbf5d7414848076cea9deb60bc002142f93b6e3131e5458760710 languageName: node linkType: hard @@ -11192,11 +12098,11 @@ __metadata: linkType: hard "wagmi@npm:^2.12.25": - version: 2.12.32 - resolution: "wagmi@npm:2.12.32" + version: 2.13.0 + resolution: "wagmi@npm:2.13.0" dependencies: - "@wagmi/connectors": "npm:5.3.10" - "@wagmi/core": "npm:2.14.6" + "@wagmi/connectors": "npm:5.5.0" + "@wagmi/core": "npm:2.15.0" use-sync-external-store: "npm:1.2.0" peerDependencies: "@tanstack/react-query": ">=5.0.0" @@ -11206,7 +12112,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/eea7ac5ffedd83d65529591a0a80873b2bf04f93906a8f29fea7704970a8c7dd732871a13f2dfcaa5e34462c2d25038596eb782142d9493bc396e18d3adf6b01 + checksum: 10c0/fb3946009dca3e244c538ac72691bc263f95f1f6eef12f6720164b515adb8d3a9af063df773307f2206fa649d5821e4bb3d152bef734b4552a5360cf9237db80 languageName: node linkType: hard @@ -11258,6 +12164,13 @@ __metadata: languageName: node linkType: hard +"webidl-conversions@npm:^4.0.2": + version: 4.0.2 + resolution: "webidl-conversions@npm:4.0.2" + checksum: 10c0/def5c5ac3479286dffcb604547628b2e6b46c5c5b8a8cfaa8c71dc3bafc85859bde5fbe89467ff861f571ab38987cf6ab3d6e7c80b39b999e50e803c12f3164f + languageName: node + linkType: hard + "whatwg-url@npm:^5.0.0": version: 5.0.0 resolution: "whatwg-url@npm:5.0.0" @@ -11268,6 +12181,17 @@ __metadata: languageName: node linkType: hard +"whatwg-url@npm:^7.0.0": + version: 7.1.0 + resolution: "whatwg-url@npm:7.1.0" + dependencies: + lodash.sortby: "npm:^4.7.0" + tr46: "npm:^1.0.1" + webidl-conversions: "npm:^4.0.2" + checksum: 10c0/2785fe4647690e5a0225a79509ba5e21fdf4a71f9de3eabdba1192483fe006fc79961198e0b99f82751557309f17fc5a07d4d83c251aa5b2f85ba71e674cbee9 + languageName: node + linkType: hard + "which-boxed-primitive@npm:^1.0.2": version: 1.0.2 resolution: "which-boxed-primitive@npm:1.0.2" @@ -11281,15 +12205,16 @@ __metadata: languageName: node linkType: hard -"which-builtin-type@npm:^1.1.3": - version: 1.1.4 - resolution: "which-builtin-type@npm:1.1.4" +"which-builtin-type@npm:^1.1.4": + version: 1.2.0 + resolution: "which-builtin-type@npm:1.2.0" dependencies: + call-bind: "npm:^1.0.7" function.prototype.name: "npm:^1.1.6" has-tostringtag: "npm:^1.0.2" is-async-function: "npm:^2.0.0" is-date-object: "npm:^1.0.5" - is-finalizationregistry: "npm:^1.0.2" + is-finalizationregistry: "npm:^1.1.0" is-generator-function: "npm:^1.0.10" is-regex: "npm:^1.1.4" is-weakref: "npm:^1.0.2" @@ -11297,7 +12222,7 @@ __metadata: which-boxed-primitive: "npm:^1.0.2" which-collection: "npm:^1.0.2" which-typed-array: "npm:^1.1.15" - checksum: 10c0/a4a76d20d869a81b1dbb4adea31edc7e6c1a4466d3ab7c2cd757c9219d48d3723b04076c85583257b0f0f8e3ebe5af337248b8ceed57b9051cb97bce5bd881d1 + checksum: 10c0/7cd4a8ccfa6a3cb7c2296c716e7266b9f31a66f3e131fe7b185232c16d3ad21442046ec1798c4ec1e19dce7eb99c7751377192e5e734dc07042d14ec0f09b332 languageName: node linkType: hard @@ -11414,7 +12339,42 @@ __metadata: languageName: node linkType: hard -"ws@npm:8.18.0, ws@npm:^8.15.1, ws@npm:^8.18.0, ws@npm:^8.8.1": +"write-file-atomic@npm:^5.0.1": + version: 5.0.1 + resolution: "write-file-atomic@npm:5.0.1" + dependencies: + imurmurhash: "npm:^0.1.4" + signal-exit: "npm:^4.0.1" + checksum: 10c0/e8c850a8e3e74eeadadb8ad23c9d9d63e4e792bd10f4836ed74189ef6e996763959f1249c5650e232f3c77c11169d239cbfc8342fc70f3fe401407d23810505d + languageName: node + linkType: hard + +"write-json-file@npm:^6.0.0": + version: 6.0.0 + resolution: "write-json-file@npm:6.0.0" + dependencies: + detect-indent: "npm:^7.0.1" + is-plain-obj: "npm:^4.1.0" + sort-keys: "npm:^5.0.0" + write-file-atomic: "npm:^5.0.1" + checksum: 10c0/3f8f0caec7948d696b1e898512547bba7b63e99eb5b67ddb74cd9ea02aa0b88d48f5b710be3b26ac3ffa8c3e58c779bd610fb349d3cec6e8fb621596a8f85069 + languageName: node + linkType: hard + +"write-package@npm:^7.1.0": + version: 7.1.0 + resolution: "write-package@npm:7.1.0" + dependencies: + deepmerge-ts: "npm:^7.1.0" + read-pkg: "npm:^9.0.1" + sort-keys: "npm:^5.0.0" + type-fest: "npm:^4.23.0" + write-json-file: "npm:^6.0.0" + checksum: 10c0/0db9ca588fc00c753409633ce7777eae4147c6b60993643399f4af0e0ce672b5943587ff5269c3adda36090065d0ef1cf2c6936a62e0d1a548dc81c6507f4f2a + languageName: node + linkType: hard + +"ws@npm:8.18.0, ws@npm:^8.18.0, ws@npm:^8.8.1": version: 8.18.0 resolution: "ws@npm:8.18.0" peerDependencies: @@ -11481,9 +12441,9 @@ __metadata: linkType: hard "xxhash-wasm@npm:^1.0.2": - version: 1.0.2 - resolution: "xxhash-wasm@npm:1.0.2" - checksum: 10c0/5ba899d9216d9897de2d61a5331b16c99226e75ce47895fc8c730bac5cb00e6e50856dd8f489c12b3012f0fc81b6894806b2e44d2eb3cc7843919793485a30d1 + version: 1.1.0 + resolution: "xxhash-wasm@npm:1.1.0" + checksum: 10c0/35aa152fc7d775ae13364fe4fb20ebd89c6ac1f56cdb6060a6d2f1ed68d15180694467e63a4adb3d11936a4798ccd75a540979070e70d9b911e9981bbdd9cea6 languageName: node linkType: hard @@ -11580,6 +12540,13 @@ __metadata: languageName: node linkType: hard +"yoctocolors@npm:^2.0.0": + version: 2.1.1 + resolution: "yoctocolors@npm:2.1.1" + checksum: 10c0/85903f7fa96f1c70badee94789fade709f9d83dab2ec92753d612d84fcea6d34c772337a9f8914c6bed2f5fc03a428ac5d893e76fab636da5f1236ab725486d0 + languageName: node + linkType: hard + "zen-observable-ts@npm:^1.2.5": version: 1.2.5 resolution: "zen-observable-ts@npm:1.2.5"