diff --git a/packages/ui/src/App.tsx b/packages/ui/src/App.tsx
index 826bdc80..56a86e91 100644
--- a/packages/ui/src/App.tsx
+++ b/packages/ui/src/App.tsx
@@ -13,6 +13,7 @@ import MainLayout from './components/layout/Main'
import { WatchedAddressesContextProvider } from './contexts/WatchedAddressesContext'
import { WalletConnectContextProvider } from './contexts/WalletConnectContext'
import { ModalsContextProvider } from './contexts/ModalsContext'
+import { PplApiContextProvider } from './contexts/PeopleChainApiContext'
const App = () => {
const queryClient = new QueryClient()
@@ -24,19 +25,21 @@ const App = () => {
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/ui/src/components/EasySetup/SetIdentity.tsx b/packages/ui/src/components/EasySetup/SetIdentity.tsx
index 12f7b728..b1992879 100644
--- a/packages/ui/src/components/EasySetup/SetIdentity.tsx
+++ b/packages/ui/src/components/EasySetup/SetIdentity.tsx
@@ -3,13 +3,13 @@ import { styled } from '@mui/material/styles'
import { SubmittableExtrinsic } from '@polkadot/api/types'
import { ISubmittableResult } from '@polkadot/types/types'
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
-import { useApi } from '../../contexts/ApiContext'
import { TextField } from '../library'
import { useIdentity } from '../../hooks/useIdentity'
import { useCheckBalance } from '../../hooks/useCheckBalance'
import { getErrorMessageReservedFunds } from '../../utils'
import { formatBnBalance } from '../../utils/formatBnBalance'
import { useSetIdentityReservedFunds } from '../../hooks/useSetIdentityReservedFunds'
+import { useIdenityApi } from '../../hooks/useIdentityApi'
interface Props {
className?: string
@@ -103,7 +103,7 @@ const fieldNameAndPlaceholder = (fieldName: keyof IdentityFields) => {
const MAX_ALLOWED_VAL_LENGTH = 32
const SetIdentity = ({ className, onSetExtrinsic, from, onSetErrorMessage }: Props) => {
- const { api, chainInfo } = useApi()
+ const { api, chainInfo } = useIdenityApi()
const [identityFields, setIdentityFields] = useState()
const chainIdentity = useIdentity(from)
const [hasChangedAtLeastAField, setHasChangedAtLeastAField] = useState(false)
diff --git a/packages/ui/src/components/modals/Send.tsx b/packages/ui/src/components/modals/Send.tsx
index bd088eab..6136542b 100644
--- a/packages/ui/src/components/modals/Send.tsx
+++ b/packages/ui/src/components/modals/Send.tsx
@@ -30,7 +30,7 @@ import { formatBnBalance } from '../../utils/formatBnBalance'
import { useGetMultisigTx } from '../../hooks/useGetMultisigTx'
import SetIdentity from '../EasySetup/SetIdentity'
import { getErrorMessageReservedFunds } from '../../utils/getErrorMessageReservedFunds'
-import { useHasIdentityPallet } from '../../hooks/useHasIdentityPallet'
+import { useHasIdentityFeature } from '../../hooks/useHasIdentityFeature'
export enum EasyTransferTitle {
SendTokens = 'Send tokens',
@@ -76,7 +76,7 @@ const Send = ({ onClose, className, onSuccess, onFinalized, preselected }: Props
(a) => !!a.address
) as AccountBaseInfo[]
}, [getMultisigAsAccountBaseInfo, selectedMultiProxy])
- const hasIdentityPallet = useHasIdentityPallet()
+ const { hasIdentityPallet, hasPplChain } = useHasIdentityFeature()
const [selectedOrigin, setSelectedOrigin] = useState(possibleOrigin[0])
const isProxySelected = useMemo(() => selectedOrigin.meta?.isProxy, [selectedOrigin])
const [selectedMultisig, setSelectedMultisig] = useState(selectedMultiProxy?.multisigs[0])
@@ -170,7 +170,7 @@ const Send = ({ onClose, className, onSuccess, onFinalized, preselected }: Props
[EasyTransferTitle.FromCallData]:
} as Partial>
- if (hasIdentityPallet) {
+ if (hasIdentityPallet && !hasPplChain) {
res[EasyTransferTitle.SetIdentity] = (
= {
polkadot: {
chainId: 'polkadot',
@@ -49,6 +53,7 @@ export const networkList: Record = {
kusama: {
chainId: 'kusama',
explorerNetworkName: 'kusama',
+ pplChainRpcUrl: kusamaPplChain,
rpcUrl: 'wss://rpc.ibp.network/kusama',
httpGraphqlUrl: HTTP_GRAPHQL_URL,
logo: chainsKusamaSVG
@@ -64,6 +69,7 @@ export const networkList: Record = {
chainId: 'asset-hub-kusama',
explorerNetworkName: 'asset-hub-kusama',
rpcUrl: 'wss://sys.ibp.network/statemine',
+ pplChainRpcUrl: kusamaPplChain,
httpGraphqlUrl: HTTP_GRAPHQL_URL,
logo: nodesAssetHubSVG
},
@@ -147,6 +153,7 @@ export const networkList: Record = {
westend: {
chainId: 'westend',
explorerNetworkName: 'westend',
+ pplChainRpcUrl: westendPplChain,
rpcUrl: 'wss://westend-rpc.polkadot.io',
httpGraphqlUrl: HTTP_GRAPHQL_URL,
logo: nodesWestendColourSVG
diff --git a/packages/ui/src/contexts/PeopleChainApiContext.tsx b/packages/ui/src/contexts/PeopleChainApiContext.tsx
new file mode 100644
index 00000000..3a4c220a
--- /dev/null
+++ b/packages/ui/src/contexts/PeopleChainApiContext.tsx
@@ -0,0 +1,99 @@
+import React, { useMemo } from 'react'
+import { ApiPromise, WsProvider } from '@polkadot/api'
+import { useState, useEffect, createContext, useContext } from 'react'
+import { useNetwork } from './NetworkContext'
+import '@polkadot/api-augment'
+
+type ApiContextProps = {
+ children: React.ReactNode | React.ReactNode[]
+}
+
+export interface IApiContext {
+ pplApi?: false | ApiPromise
+ pplChainInfo?: ChainInfoHuman
+}
+
+export interface ChainInfoHuman {
+ ss58Format: number
+ tokenDecimals: number
+ tokenSymbol: string
+}
+
+interface RawChainInfoHuman {
+ ss58Format: string
+ tokenDecimals: string[]
+ tokenSymbol: string[]
+}
+
+const PplApiContext = createContext(undefined)
+
+const PplApiContextProvider = ({ children }: ApiContextProps) => {
+ const { selectedNetworkInfo } = useNetwork()
+ const [chainInfo, setChainInfo] = useState()
+ const [pplApiPromise, setPplApiPromise] = useState()
+ const [isPplApiReady, setIsPplApiReady] = useState(false)
+ const provider = useMemo(
+ () =>
+ !!selectedNetworkInfo?.pplChainRpcUrl && new WsProvider(selectedNetworkInfo?.pplChainRpcUrl),
+ [selectedNetworkInfo]
+ )
+
+ useEffect(() => {
+ if (!provider) return
+
+ // console.log('---> connecting to', provider.endpoint)
+ setIsPplApiReady(false)
+ const pplApi = new ApiPromise({ provider })
+ pplApi.isReady.then((newApi) => setPplApiPromise(newApi)).catch(console.error)
+
+ return () => {
+ // console.log('<---disconnecting')
+ setIsPplApiReady(false)
+ !!pplApi && pplApi.disconnect()
+ setPplApiPromise(undefined)
+ }
+
+ // prevent an infinite loop
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [provider])
+
+ useEffect(() => {
+ if (!pplApiPromise) return
+
+ pplApiPromise.isReady
+ .then((pplApi) => {
+ setIsPplApiReady(true)
+
+ const info = pplApi.registry.getChainProperties()
+ const raw = info?.toHuman() as unknown as RawChainInfoHuman
+ setChainInfo({
+ // some parachains such as interlay have a comma in the format, e.g: "2,042"
+ ss58Format: Number(raw?.ss58Format.replace(',', '')) || 0,
+ tokenDecimals: Number(raw?.tokenDecimals[0]) || 0,
+ tokenSymbol: raw?.tokenSymbol[0] || ''
+ })
+ })
+ .catch(console.error)
+ }, [pplApiPromise])
+
+ return (
+
+ {children}
+
+ )
+}
+
+const usePplApi = () => {
+ const context = useContext(PplApiContext)
+ if (context === undefined) {
+ throw new Error('usePplApi must be used within a PplApiContextProvider')
+ }
+ return context
+}
+
+export { PplApiContextProvider, usePplApi }
diff --git a/packages/ui/src/hooks/useGetIdentity.tsx b/packages/ui/src/hooks/useGetIdentity.tsx
index de0d589e..a35404dc 100644
--- a/packages/ui/src/hooks/useGetIdentity.tsx
+++ b/packages/ui/src/hooks/useGetIdentity.tsx
@@ -1,8 +1,8 @@
import { useCallback } from 'react'
-import { useApi } from '../contexts/ApiContext'
+import { useIdenityApi } from './useIdentityApi'
export const useGetIdentity = () => {
- const { api } = useApi()
+ const { api } = useIdenityApi()
const getIdentity = useCallback(
async (address: string) => {
diff --git a/packages/ui/src/hooks/useHasIdentityFeature.tsx b/packages/ui/src/hooks/useHasIdentityFeature.tsx
new file mode 100644
index 00000000..d7c151df
--- /dev/null
+++ b/packages/ui/src/hooks/useHasIdentityFeature.tsx
@@ -0,0 +1,14 @@
+import { useMemo } from 'react'
+import { useNetwork } from '../contexts/NetworkContext'
+import { useIdenityApi } from './useIdentityApi'
+
+export const useHasIdentityFeature = () => {
+ const { api } = useIdenityApi()
+ const { selectedNetworkInfo } = useNetwork()
+ const hasIdentityPallet = useMemo(() => !!api && !!api.tx?.identity?.setIdentity, [api])
+ const hasPplChain = useMemo(() => !!selectedNetworkInfo?.pplChainRpcUrl, [selectedNetworkInfo])
+ return {
+ hasPplChain,
+ hasIdentityPallet
+ }
+}
diff --git a/packages/ui/src/hooks/useHasIdentityPallet.tsx b/packages/ui/src/hooks/useHasIdentityPallet.tsx
deleted file mode 100644
index bfe83cb9..00000000
--- a/packages/ui/src/hooks/useHasIdentityPallet.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import { useMemo } from 'react'
-import { useApi } from '../contexts/ApiContext'
-
-export const useHasIdentityPallet = () => {
- const { api } = useApi()
- const hasIdentityPallet = useMemo(() => !!api && !!api.tx?.identity?.setIdentity, [api])
-
- return hasIdentityPallet
-}
diff --git a/packages/ui/src/hooks/useIdentity.tsx b/packages/ui/src/hooks/useIdentity.tsx
index 67d1075d..d3bd82e6 100644
--- a/packages/ui/src/hooks/useIdentity.tsx
+++ b/packages/ui/src/hooks/useIdentity.tsx
@@ -1,9 +1,9 @@
import { useEffect, useState } from 'react'
-import { useApi } from '../contexts/ApiContext'
import { DeriveAccountInfo, DeriveAccountRegistration } from '@polkadot/api-derive/types'
+import { useIdenityApi } from './useIdentityApi'
export const useIdentity = (address?: string) => {
- const { api } = useApi()
+ const { api } = useIdenityApi()
const [identity, setIdentity] = useState(null)
useEffect(() => {
diff --git a/packages/ui/src/hooks/useIdentityApi.tsx b/packages/ui/src/hooks/useIdentityApi.tsx
new file mode 100644
index 00000000..15a7d1fd
--- /dev/null
+++ b/packages/ui/src/hooks/useIdentityApi.tsx
@@ -0,0 +1,39 @@
+import { ApiPromise } from '@polkadot/api'
+import { useState, useEffect } from 'react'
+import { useApi } from '../contexts/ApiContext'
+import { ChainInfoHuman, usePplApi } from '../contexts/PeopleChainApiContext'
+
+export const useIdenityApi = () => {
+ const { api, chainInfo } = useApi()
+ const { pplApi, pplChainInfo } = usePplApi()
+ const [apiToUse, setApiToUse] = useState(null)
+ const [chainInfoToUse, setChainInfoToUse] = useState(undefined)
+
+ useEffect(() => {
+ if (!pplApi && !api) {
+ return
+ }
+
+ if (pplApi) {
+ setApiToUse(pplApi)
+ setChainInfoToUse(pplChainInfo)
+ } else if (api) {
+ setApiToUse(api)
+ setChainInfoToUse(chainInfo)
+ }
+ }, [api, chainInfo, pplApi, pplChainInfo])
+
+ useEffect(() => {
+ if (!pplApi && !api) {
+ return
+ }
+
+ if (pplApi) {
+ setApiToUse(pplApi)
+ } else if (api) {
+ setApiToUse(api)
+ }
+ }, [api, pplApi])
+
+ return { api: apiToUse, chainInfo: chainInfoToUse }
+}
diff --git a/packages/ui/src/pages/Home/MultisigActionMenu.tsx b/packages/ui/src/pages/Home/MultisigActionMenu.tsx
index 09034dd6..18b67ba5 100644
--- a/packages/ui/src/pages/Home/MultisigActionMenu.tsx
+++ b/packages/ui/src/pages/Home/MultisigActionMenu.tsx
@@ -11,7 +11,7 @@ import {
} from 'react-icons/hi2'
import { useGetSubscanLinks } from '../../hooks/useSubscanLink'
import { EasyTransferTitle } from '../../components/modals/Send'
-import { useHasIdentityPallet } from '../../hooks/useHasIdentityPallet'
+import { useHasIdentityFeature } from '../../hooks/useHasIdentityFeature'
interface MultisigActionMenuProps {
withNewTransactionButton?: boolean
@@ -25,7 +25,7 @@ const MultisigActionMenu = ({
const { selectedHasProxy, selectedIsWatched, selectedMultiProxy } = useMultiProxy()
const { setIsEditModalOpen, setIsChangeMultiModalOpen, onOpenSendModal } = useModals()
const { getSubscanAccountLink } = useGetSubscanLinks()
- const hasIdentityPallet = useHasIdentityPallet()
+ const { hasIdentityPallet, hasPplChain } = useHasIdentityFeature()
const options: MenuOption[] = useMemo(() => {
const opts = [
@@ -48,6 +48,7 @@ const MultisigActionMenu = ({
!selectedIsWatched &&
hasIdentityPallet &&
+ hasPplChain &&
opts.push({
text: 'Set identity',
icon: ,