From 841a72836cf28aa82ffa770df8958b38b56b096e Mon Sep 17 00:00:00 2001 From: Thibaut Sardan Date: Fri, 17 Jan 2025 11:30:48 +0100 Subject: [PATCH] using type guards with 2 non hydration lists --- packages/ui/src/components/CallInfo.tsx | 4 +- .../src/components/modals/ChangeMultisig.tsx | 248 +++++++----------- packages/ui/src/contexts/ApiContext.tsx | 54 +++- packages/ui/src/hooks/useIdentityApi.tsx | 8 +- packages/ui/src/hooks/usePendingTx.tsx | 8 +- packages/ui/src/utils/getApproveAsMultiTx.ts | 4 +- packages/ui/src/utils/getAsMultiTx.ts | 6 +- .../src/utils/getEncodedCallFromDecodedTx.ts | 4 +- packages/ui/tsconfig.json | 3 +- 9 files changed, 160 insertions(+), 179 deletions(-) diff --git a/packages/ui/src/components/CallInfo.tsx b/packages/ui/src/components/CallInfo.tsx index 41ebb387..b1bd74a0 100644 --- a/packages/ui/src/components/CallInfo.tsx +++ b/packages/ui/src/components/CallInfo.tsx @@ -1,7 +1,7 @@ import Expander from './Expander' import { styled } from '@mui/material/styles' import { ReactNode, useMemo } from 'react' -import { IApiContext, useApi } from '../contexts/ApiContext' +import { ApiDescriptors, IApiContext, useApi } from '../contexts/ApiContext' import { getExtrinsicName } from '../utils/getExtrinsicName' import { isProxyCall } from '../utils/isProxyCall' import { formatBigIntBalance } from '../utils/formatBnBalance' @@ -29,7 +29,7 @@ interface CreateTreeParams { decimals: number unit: string name?: string - api: IApiContext['api'] + api: IApiContext['api'] chainInfo?: ChainInfoHuman } diff --git a/packages/ui/src/components/modals/ChangeMultisig.tsx b/packages/ui/src/components/modals/ChangeMultisig.tsx index 2f0b38da..1a1974f4 100644 --- a/packages/ui/src/components/modals/ChangeMultisig.tsx +++ b/packages/ui/src/components/modals/ChangeMultisig.tsx @@ -14,7 +14,13 @@ import AccountDisplay from '../AccountDisplay/AccountDisplay' import ThresholdSelection from '../../pages/Creation/ThresholdSelection' import SignatorySelection from '../select/SignatorySelection' import Summary from '../../pages/Creation/Summary' -import { useApi } from '../../contexts/ApiContext' +import { + isContextIn, + isContextOf, + noHydrationKeys_1, + noHydrationKeys_2, + useApi +} from '../../contexts/ApiContext' import { useAccounts } from '../../contexts/AccountsContext' import { useSigningCallback } from '../../hooks/useSigningCallback' import { AccountBadge } from '../../types' @@ -44,7 +50,8 @@ type Step = 'selection' | 'summary' | 'call1' | 'call2' const ChangeMultisig = ({ onClose, className }: Props) => { const { selectedNetwork } = useNetwork() const modalRef = useRef(null) - const { api, chainInfo, compatibilityToken, apiDescriptor } = useApi() + const ctx = useApi() + const { api, chainInfo, compatibilityToken } = ctx const { selectedMultiProxy, getMultisigAsAccountBaseInfo, getMultisigByAddress } = useMultiProxy() const signCallBack2 = useSigningCallback({ onSuccess: onClose, @@ -89,7 +96,7 @@ const ChangeMultisig = ({ onClose, className }: Props) => { const [callError, setCallError] = useState('') const secondCall = useMemo(() => { - if (!api || !compatibilityToken || !selectedNetwork) { + if (!ctx.api || !compatibilityToken || !selectedNetwork) { // console.error('api is not ready') return } @@ -122,79 +129,47 @@ const ChangeMultisig = ({ onClose, className }: Props) => { selectedMultisig.signatories.filter((sig) => sig !== selectedAccount.address) ) - const addProxyTx = - apiDescriptor === 'hydration' - ? api.tx.Proxy.add_proxy({ - delegate: newMultisigAddress, + const addProxyTx = isContextOf(ctx, 'hydration') + ? ctx.api.tx.Proxy.add_proxy({ + delegate: newMultisigAddress, + proxy_type: Enum('Any'), + delay: 0 + }) + : isContextIn(ctx, noHydrationKeys_1) + ? ctx.api.tx.Proxy.add_proxy({ + delegate: MultiAddress.Id(newMultisigAddress), proxy_type: Enum('Any'), delay: 0 }) - : apiDescriptor === 'acala' - ? api.tx.Proxy.add_proxy({ - delegate: MultiAddress.Id(newMultisigAddress), - proxy_type: Enum('Any'), - delay: 0 - }) - : apiDescriptor === 'khala' - ? api.tx.Proxy.add_proxy({ - delegate: MultiAddress.Id(newMultisigAddress), - proxy_type: Enum('Any'), - delay: 0 - }) - : apiDescriptor === 'kusama' - ? api.tx.Proxy.add_proxy({ - delegate: MultiAddress.Id(newMultisigAddress), - proxy_type: Enum('Any'), - delay: 0 - }) - : apiDescriptor === 'phala' - ? api.tx.Proxy.add_proxy({ - delegate: MultiAddress.Id(newMultisigAddress), - proxy_type: Enum('Any'), - delay: 0 - }) - : api.tx.Proxy.add_proxy({ - delegate: MultiAddress.Id(newMultisigAddress), - proxy_type: Enum('Any'), - delay: 0 - }) - - const proxyTx = - apiDescriptor === 'hydration' - ? api.tx.Proxy.proxy({ - real: selectedMultiProxy?.proxy, + : isContextIn(ctx, noHydrationKeys_2) && + ctx.api.tx.Proxy.add_proxy({ + delegate: MultiAddress.Id(newMultisigAddress), + proxy_type: Enum('Any'), + delay: 0 + }) + + if (!addProxyTx) return + + const proxyTx = isContextOf(ctx, 'hydration') + ? ctx.api.tx.Proxy.proxy({ + real: selectedMultiProxy?.proxy, + force_proxy_type: undefined, + call: addProxyTx.decodedCall + }) + : isContextIn(ctx, noHydrationKeys_1) + ? ctx.api.tx.Proxy.proxy({ + real: MultiAddress.Id(selectedMultiProxy?.proxy), + force_proxy_type: undefined, + call: addProxyTx.decodedCall + }) + : isContextIn(ctx, noHydrationKeys_2) && + ctx.api.tx.Proxy.proxy({ + real: MultiAddress.Id(selectedMultiProxy?.proxy), force_proxy_type: undefined, call: addProxyTx.decodedCall }) - : apiDescriptor === 'acala' - ? api.tx.Proxy.proxy({ - real: MultiAddress.Id(selectedMultiProxy?.proxy), - force_proxy_type: undefined, - call: addProxyTx.decodedCall - }) - : apiDescriptor === 'khala' - ? api.tx.Proxy.proxy({ - real: MultiAddress.Id(selectedMultiProxy?.proxy), - force_proxy_type: undefined, - call: addProxyTx.decodedCall - }) - : apiDescriptor === 'kusama' - ? api.tx.Proxy.proxy({ - real: MultiAddress.Id(selectedMultiProxy?.proxy), - force_proxy_type: undefined, - call: addProxyTx.decodedCall - }) - : apiDescriptor === 'phala' - ? api.tx.Proxy.proxy({ - real: MultiAddress.Id(selectedMultiProxy?.proxy), - force_proxy_type: undefined, - call: addProxyTx.decodedCall - }) - : api.tx.Proxy.proxy({ - real: MultiAddress.Id(selectedMultiProxy?.proxy), - force_proxy_type: undefined, - call: addProxyTx.decodedCall - }) + + if (!proxyTx) return // call with the old multisig to delete the new one return getAsMultiTx({ @@ -205,22 +180,22 @@ const ChangeMultisig = ({ onClose, className }: Props) => { compatibilityToken }) }, [ - api, - chainInfo, + ctx, compatibilityToken, - getSortAddress, - newMultisigAddress, - newThreshold, - oldThreshold, + selectedNetwork, selectedAccount, selectedMultiProxy?.proxy, + chainInfo, selectedMultisig, - selectedNetwork, - apiDescriptor + oldThreshold, + newThreshold, + newMultisigAddress, + getSortAddress, + api ]) const firstCall = useMemo(() => { - if (!api || !compatibilityToken) { + if (!ctx?.api || !api || !compatibilityToken) { // console.error('api is not ready') return } @@ -248,79 +223,48 @@ const ChangeMultisig = ({ onClose, className }: Props) => { const otherNewSignatories = getSortAddress( newSignatories.filter((sig) => sig !== selectedAccount.address) ) - const removeProxyTx = - apiDescriptor === 'hydration' - ? api.tx.Proxy.remove_proxy({ - delegate: selectedMultisig?.address, + + const removeProxyTx = isContextOf(ctx, 'hydration') + ? ctx.api.tx.Proxy.remove_proxy({ + delegate: selectedMultisig?.address, + proxy_type: Enum('Any'), + delay: 0 + }) + : isContextIn(ctx, noHydrationKeys_1) + ? ctx.api.tx.Proxy.remove_proxy({ + delegate: MultiAddress.Id(selectedMultisig?.address), + proxy_type: Enum('Any'), + delay: 0 + }) + : isContextIn(ctx, noHydrationKeys_2) && + ctx.api.tx.Proxy.remove_proxy({ + delegate: MultiAddress.Id(selectedMultisig?.address), proxy_type: Enum('Any'), delay: 0 }) - : apiDescriptor === 'acala' - ? api.tx.Proxy.remove_proxy({ - delegate: MultiAddress.Id(selectedMultisig?.address), - proxy_type: Enum('Any'), - delay: 0 - }) - : apiDescriptor === 'khala' - ? api.tx.Proxy.remove_proxy({ - delegate: MultiAddress.Id(selectedMultisig?.address), - proxy_type: Enum('Any'), - delay: 0 - }) - : apiDescriptor === 'kusama' - ? api.tx.Proxy.remove_proxy({ - delegate: MultiAddress.Id(selectedMultisig?.address), - proxy_type: Enum('Any'), - delay: 0 - }) - : apiDescriptor === 'phala' - ? api.tx.Proxy.remove_proxy({ - delegate: MultiAddress.Id(selectedMultisig?.address), - proxy_type: Enum('Any'), - delay: 0 - }) - : api.tx.Proxy.remove_proxy({ - delegate: MultiAddress.Id(selectedMultisig?.address), - proxy_type: Enum('Any'), - delay: 0 - }) - - const proxyTx = - apiDescriptor === 'hydration' - ? api.tx.Proxy.proxy({ - real: selectedMultiProxy?.proxy, + + if (!removeProxyTx) return + + const proxyTx = isContextOf(ctx, 'hydration') + ? ctx.api.tx.Proxy.proxy({ + real: selectedMultiProxy?.proxy, + force_proxy_type: undefined, + call: removeProxyTx.decodedCall + }) + : isContextIn(ctx, noHydrationKeys_1) + ? ctx.api.tx.Proxy.proxy({ + real: MultiAddress.Id(selectedMultiProxy?.proxy), force_proxy_type: undefined, call: removeProxyTx.decodedCall }) - : apiDescriptor === 'acala' - ? api.tx.Proxy.proxy({ - real: MultiAddress.Id(selectedMultiProxy?.proxy), - force_proxy_type: undefined, - call: removeProxyTx.decodedCall - }) - : apiDescriptor === 'khala' - ? api.tx.Proxy.proxy({ - real: MultiAddress.Id(selectedMultiProxy?.proxy), - force_proxy_type: undefined, - call: removeProxyTx.decodedCall - }) - : apiDescriptor === 'kusama' - ? api.tx.Proxy.proxy({ - real: MultiAddress.Id(selectedMultiProxy?.proxy), - force_proxy_type: undefined, - call: removeProxyTx.decodedCall - }) - : apiDescriptor === 'phala' - ? api.tx.Proxy.proxy({ - real: MultiAddress.Id(selectedMultiProxy?.proxy), - force_proxy_type: undefined, - call: removeProxyTx.decodedCall - }) - : api.tx.Proxy.proxy({ - real: MultiAddress.Id(selectedMultiProxy?.proxy), - force_proxy_type: undefined, - call: removeProxyTx.decodedCall - }) + : isContextIn(ctx, noHydrationKeys_2) && + api.tx.Proxy.proxy({ + real: MultiAddress.Id(selectedMultiProxy?.proxy), + force_proxy_type: undefined, + call: removeProxyTx.decodedCall + }) + + if (!proxyTx) return return getAsMultiTx({ api, @@ -330,16 +274,16 @@ const ChangeMultisig = ({ onClose, className }: Props) => { compatibilityToken }) }, [ + ctx, api, + compatibilityToken, + selectedAccount, + selectedMultisig?.address, + selectedMultiProxy?.proxy, chainInfo, - getSortAddress, newSignatories, newThreshold, - selectedAccount, - selectedMultiProxy, - selectedMultisig, - compatibilityToken, - apiDescriptor + getSortAddress ]) const { multisigProposalNeededFunds: firstCallNeededFunds, reserved: firstCallReserved } = diff --git a/packages/ui/src/contexts/ApiContext.tsx b/packages/ui/src/contexts/ApiContext.tsx index 6f9f3b89..049bb1b0 100644 --- a/packages/ui/src/contexts/ApiContext.tsx +++ b/packages/ui/src/contexts/ApiContext.tsx @@ -21,7 +21,7 @@ import { westend } from '@polkadot-api/descriptors' -export const DESCRIPTORS_first = { +export const DESCRIPTORS = { acala, bifrostDot, dot, @@ -33,33 +33,69 @@ export const DESCRIPTORS_first = { paseo, phala, polimec, - coretimeDot + coretimeDot, + westend } as const -export const DESCRIPTORS_seconds = { - westend +export const DESCRIPTORS_NOT_HYDRATION_1 = { + acala, + bifrostDot, + dot, + dotAssetHub, + khala, + phala, + paseo, + polimec } as const -export const DESCRIPTORS = { - ...DESCRIPTORS_first, - ...DESCRIPTORS_seconds -} +export const DESCRIPTORS_NOT_HYDRATION_2 = { + ksm, + ksmAssetHub, + coretimeDot, + westend +} as const export type ApiDescriptors = keyof typeof DESCRIPTORS +export type ApiDescriptorsNotHydration_1 = keyof typeof DESCRIPTORS_NOT_HYDRATION_1 +export type ApiDescriptorsNotHydration_2 = keyof typeof DESCRIPTORS_NOT_HYDRATION_1 + export type Descriptors = (typeof DESCRIPTORS)[Id] +type ApiOf = TypedApi> + +export const noHydrationKeys_1 = Object.keys( + DESCRIPTORS_NOT_HYDRATION_1 +) as ApiDescriptorsNotHydration_1[] + +export const noHydrationKeys_2 = Object.keys( + DESCRIPTORS_NOT_HYDRATION_2 +) as ApiDescriptorsNotHydration_2[] type ApiContextProps = { children: React.ReactNode | React.ReactNode[] } export type IApiContext = { - api?: TypedApi> apiDescriptor?: Id + api?: ApiOf chainInfo?: ChainInfoHuman client?: PolkadotClient compatibilityToken?: CompatibilityToken } +export const isContextOf = ( + ctx: unknown, + descriptor: Id +): ctx is IApiContext => { + return !!ctx && (ctx as IApiContext).apiDescriptor === descriptor +} + +export const isContextIn = ( + api: unknown, + descriptors: Id[] +): api is IApiContext => { + return descriptors.some((descriptor) => isContextOf(api, descriptor)) +} + interface ChainInfoHuman { ss58Format: number tokenDecimals: number diff --git a/packages/ui/src/hooks/useIdentityApi.tsx b/packages/ui/src/hooks/useIdentityApi.tsx index bc57bfc7..cb6ac383 100644 --- a/packages/ui/src/hooks/useIdentityApi.tsx +++ b/packages/ui/src/hooks/useIdentityApi.tsx @@ -1,13 +1,13 @@ import { useState, useEffect } from 'react' import { ChainInfoHuman, IPplApiContext, usePplApi } from '../contexts/PeopleChainApiContext' -import { IApiContext, useApi } from '../contexts/ApiContext' +import { ApiDescriptors, IApiContext, useApi } from '../contexts/ApiContext' export const useIdenityApi = () => { const { pplApi, pplChainInfo } = usePplApi() const { api, chainInfo } = useApi() - const [apiToUse, setApiToUse] = useState( - null - ) + const [apiToUse, setApiToUse] = useState< + IPplApiContext['pplApi'] | IApiContext['api'] | null + >(null) const [chainInfoToUse, setChainInfoToUse] = useState(undefined) useEffect(() => { diff --git a/packages/ui/src/hooks/usePendingTx.tsx b/packages/ui/src/hooks/usePendingTx.tsx index 33efa7cb..db403209 100644 --- a/packages/ui/src/hooks/usePendingTx.tsx +++ b/packages/ui/src/hooks/usePendingTx.tsx @@ -5,7 +5,7 @@ import { useMultisigCallQuery } from './useQueryMultisigCalls' import { isEmptyArray } from '../utils/arrayUtils' import { isProxyCall } from '../utils/isProxyCall' import { useAccountId } from './useAccountId' -import { IApiContext, useApi } from '../contexts/ApiContext' +import { ApiDescriptors, IApiContext, useApi } from '../contexts/ApiContext' import dayjs from 'dayjs' import localizedFormat from 'dayjs/plugin/localizedFormat' import { PolkadotClient, Transaction } from 'polkadot-api' @@ -38,7 +38,7 @@ type AggGroupedByDate = { [index: string]: CallDataInfoFromChain[] } const opaqueMetadata = Tuple(compact, Bin(Infinity)).dec const getExtDecoderAt = async ( - api: IApiContext['api'], + api: IApiContext['api'], client: PolkadotClient, blockHash?: string ) => { @@ -59,7 +59,7 @@ const getExtDecoderAt = async ( const getMultisigInfo = async ( call: Transaction['decodedCall'], - api: IApiContext['api'] + api: IApiContext['api'] ): Promise[]> => { if (!api) return [] @@ -111,7 +111,7 @@ const getMultisigInfo = async ( const getCallDataFromChainPromise = ( pendingTxData: PendingTx[], - api: IApiContext['api'], + api: IApiContext['api'], client: PolkadotClient ) => pendingTxData.map(async (pendingTx) => { diff --git a/packages/ui/src/utils/getApproveAsMultiTx.ts b/packages/ui/src/utils/getApproveAsMultiTx.ts index 3fca8a2d..5c4a8b1c 100644 --- a/packages/ui/src/utils/getApproveAsMultiTx.ts +++ b/packages/ui/src/utils/getApproveAsMultiTx.ts @@ -1,9 +1,9 @@ import { FixedSizeBinary, HexString } from 'polkadot-api' -import { IApiContext } from '../contexts/ApiContext' +import { ApiDescriptors, IApiContext } from '../contexts/ApiContext' import { MultisigStorageInfo } from '../types' interface Params { - api: IApiContext['api'] + api: IApiContext['api'] threshold: number otherSignatories: string[] when?: MultisigStorageInfo['when'] diff --git a/packages/ui/src/utils/getAsMultiTx.ts b/packages/ui/src/utils/getAsMultiTx.ts index ec9c6a81..73768aa7 100644 --- a/packages/ui/src/utils/getAsMultiTx.ts +++ b/packages/ui/src/utils/getAsMultiTx.ts @@ -1,16 +1,16 @@ import { MultisigStorageInfo, Weight } from '../types' import { Binary, HexString, Transaction } from 'polkadot-api' -import { IApiContext } from '../contexts/ApiContext' +import { ApiDescriptors, IApiContext } from '../contexts/ApiContext' interface Params { - api: IApiContext['api'] + api: IApiContext['api'] threshold: number otherSignatories: string[] tx?: Transaction callData?: HexString weight?: Weight when?: MultisigStorageInfo['when'] - compatibilityToken: IApiContext['compatibilityToken'] + compatibilityToken: IApiContext['compatibilityToken'] } // TODO check if we can do this with papi diff --git a/packages/ui/src/utils/getEncodedCallFromDecodedTx.ts b/packages/ui/src/utils/getEncodedCallFromDecodedTx.ts index 7dcec3b3..c267ed46 100644 --- a/packages/ui/src/utils/getEncodedCallFromDecodedTx.ts +++ b/packages/ui/src/utils/getEncodedCallFromDecodedTx.ts @@ -1,8 +1,8 @@ import { CompatibilityToken, Transaction } from 'polkadot-api' -import { IApiContext } from '../contexts/ApiContext' +import { ApiDescriptors, IApiContext } from '../contexts/ApiContext' export const getEncodedCallFromDecodedTx = ( - api: IApiContext['api'], + api: IApiContext['api'], decodedTx: Transaction['decodedCall'], compatibilityToken: CompatibilityToken ) => { diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json index d2450b2a..edd3650f 100644 --- a/packages/ui/tsconfig.json +++ b/packages/ui/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { - "composite": true, + // commenting it out to make TS compiler happy with Papi types + // "composite": true, "target": "esnext", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true,