Skip to content

Commit

Permalink
Refactor address encoding/decoding (#323)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tbaut authored Aug 29, 2023
1 parent fb85d39 commit f929610
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 92 deletions.
19 changes: 4 additions & 15 deletions packages/ui/src/components/AccountDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { useApi } from '../contexts/ApiContext'
import { DeriveAccountInfo, DeriveAccountRegistration } from '@polkadot/api-derive/types'
import IdentityIcon from './IdentityIcon'
import { useGetBalance } from '../hooks/useGetBalance'
import { encodeAddress } from '@polkadot/util-crypto'
import { useGetEncodedAddress } from '../hooks/useGetEncodedAddress'

interface Props {
address: string
Expand All @@ -32,22 +32,11 @@ const AccountDisplay = ({
const { balanceFormatted } = useGetBalance({ address })
const localName = useMemo(() => getNamesWithExtension(address), [address, getNamesWithExtension])
const [identity, setIdentity] = useState<DeriveAccountRegistration | null>(null)
const { api, chainInfo } = useApi()
const { api } = useApi()
const [mainDisplay, setMainDisplay] = useState<string>('')
const [sub, setSub] = useState<string | null>(null)
const [encodedAddress, setEncodedAddress] = useState('')

useEffect(() => {
if (!chainInfo) {
return
}

try {
setEncodedAddress(encodeAddress(address, chainInfo.ss58Format))
} catch (e) {
console.error(`Error encoding the address ${address}, skipping`, e)
}
}, [address, chainInfo, encodedAddress])
const getEncodedAddress = useGetEncodedAddress()
const encodedAddress = useMemo(() => getEncodedAddress(address), [address, getEncodedAddress])

useEffect(() => {
if (!api) {
Expand Down
27 changes: 11 additions & 16 deletions packages/ui/src/components/modals/ChangeMultisig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import SignatorySelection from '../select/SignatorySelection'
import Summary from '../../pages/Creation/Summary'
import { useApi } from '../../contexts/ApiContext'
import { useAccounts } from '../../contexts/AccountsContext'
import { createKeyMulti, encodeAddress, sortAddresses } from '@polkadot/util-crypto'
import { createKeyMulti, sortAddresses } from '@polkadot/util-crypto'
import { useSigningCallback } from '../../hooks/useSigningCallback'
import { useToasts } from '../../contexts/ToastContext'
import { AccountBadge } from '../../types'
Expand All @@ -30,6 +30,7 @@ import { MdErrorOutline as ErrorOutlineIcon } from 'react-icons/md'
import { useGetSubscanLinks } from '../../hooks/useSubscanLink'
import { Button } from '../library'
import { ModalCloseButton } from '../library/ModalCloseButton'
import { useGetEncodedAddress } from '../../hooks/useGetEncodedAddress'

interface Props {
onClose: () => void
Expand All @@ -44,6 +45,7 @@ const ChangeMultisig = ({ onClose, className }: Props) => {
const { api, chainInfo } = useApi()
const { selectedMultiProxy, getMultisigAsAccountBaseInfo, getMultisigByAddress } = useMultiProxy()
const { addToast } = useToasts()
const getEncodedAddress = useGetEncodedAddress()
const signCallBack2 = useSigningCallback({
onSuccess: onClose,
onError: onClose
Expand All @@ -65,21 +67,14 @@ const ChangeMultisig = ({ onClose, className }: Props) => {
).length > 0,
[ownAddressList, newSignatories, selectedMultisig?.signatories]
)
const newMultisigAddress = useMemo(() => {
if (!newThreshold || !chainInfo) return

const multisigPubKey = createKeyMulti(newSignatories, newThreshold)
let newMultiAddress: string | undefined
try {
newMultiAddress = encodeAddress(multisigPubKey, chainInfo.ss58Format)
} catch (e) {
console.error(`Error encoding the address ${multisigPubKey}`, e)
return
}

return newMultiAddress
}, [chainInfo, newSignatories, newThreshold])

const newMultisigPubKey = useMemo(() => {
if (!newThreshold) return
return createKeyMulti(newSignatories, newThreshold)
}, [newSignatories, newThreshold])
const newMultisigAddress = useMemo(
() => getEncodedAddress(newMultisigPubKey),
[getEncodedAddress, newMultisigPubKey]
)
const canGoNext = useMemo(
() => newMultisigAddress !== selectedMultisig?.address,
[newMultisigAddress, selectedMultisig]
Expand Down
12 changes: 9 additions & 3 deletions packages/ui/src/components/select/AccountSelection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Button, InputField } from '../library'
import GenericAccountSelection, { AccountBaseInfo } from './GenericAccountSelection'
import { useAccountBaseFromAccountList } from '../../hooks/useAccountBaseFromAccountList'
import { getOptionLabel } from '../../utils/getOptionLabel'
import { getPubKeyFromAddress } from '../../utils/getPubKeyFromAddress'

interface Props {
className?: string
Expand Down Expand Up @@ -43,6 +44,10 @@ const AccountSelection = ({
const dedupedSignatories = useMemo(() => {
return accountBase.filter((account) => !currentSelection.includes(account.address))
}, [accountBase, currentSelection])
const currentSelectionPubKeys = useMemo(
() => getPubKeyFromAddress(currentSelection),
[currentSelection]
)
const extensionName = useMemo(() => {
if (!selected) return ''
return getAccountByAddress(selected.address)?.meta.name
Expand All @@ -69,8 +74,9 @@ const AccountSelection = ({
return
}

if (currentSelection.includes(selected.address)) {
setErrorMessage('Signatory already added')
const pubkey = getPubKeyFromAddress(selected.address)
if (pubkey && (currentSelectionPubKeys as string[]).includes(pubkey)) {
setErrorMessage('Account already added')
return
}

Expand All @@ -80,7 +86,7 @@ const AccountSelection = ({
setSelected(undefined)
setName('')
}
}, [addName, addAccount, currentSelection, name, selected])
}, [selected, currentSelectionPubKeys, addAccount, name, addName])

const handleSpecialKeys = useCallback(
(e: any) => {
Expand Down
26 changes: 7 additions & 19 deletions packages/ui/src/contexts/WatchedAddressesContext.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { createContext, useCallback, useContext, useEffect, useState } from 'react'
import { useApi } from './ApiContext'
import { encodeAccounts } from '../utils/encodeAccounts'
import { decodeAddress, encodeAddress } from '@polkadot/util-crypto'
import { u8aToHex } from '@polkadot/util'
import { getPubKeyFromAddress } from '../utils/getPubKeyFromAddress'
import { useGetEncodedAddress } from '../hooks/useGetEncodedAddress'

const LOCALSTORAGE_WATCHED_ACCOUNTS_KEY = 'multix.watchedAccount'

Expand All @@ -22,6 +22,7 @@ const WatchedAddressesContextProvider = ({ children }: WatchedAddressesProps) =>
const [watchedAddresses, setWatchedAddresses] = useState<string[]>([])
const [isInitialized, setIsInitialized] = useState(false)
const { chainInfo } = useApi()
const getEncodedAddress = useGetEncodedAddress()

// update the current account list with the right network prefix
// this will run for every network change
Expand All @@ -35,14 +36,10 @@ const WatchedAddressesContextProvider = ({ children }: WatchedAddressesProps) =>

const addWatchedAccount = useCallback(
(address: string) => {
try {
const encodedAddress = encodeAddress(address, chainInfo?.ss58Format)
setWatchedAddresses((prev) => [...prev, encodedAddress])
} catch (e) {
console.error(`Error encoding the address ${address}, skipping`, e)
}
const encodedAddress = getEncodedAddress(address)
encodedAddress && setWatchedAddresses((prev) => [...prev, encodedAddress])
},
[chainInfo]
[getEncodedAddress]
)

const removeWatchedAccount = useCallback(
Expand Down Expand Up @@ -77,16 +74,7 @@ const WatchedAddressesContextProvider = ({ children }: WatchedAddressesProps) =>
useEffect(() => {
if (!isInitialized) return

const pubKeyArray = watchedAddresses
.map((address) => {
try {
return u8aToHex(decodeAddress(address))
} catch (e) {
console.error(`Error decoding the address ${address}, skipping`, e)
return undefined
}
})
.filter((address) => !!address)
const pubKeyArray = getPubKeyFromAddress(watchedAddresses)

localStorage.setItem(LOCALSTORAGE_WATCHED_ACCOUNTS_KEY, JSON.stringify(pubKeyArray))
}, [isInitialized, watchedAddresses])
Expand Down
20 changes: 20 additions & 0 deletions packages/ui/src/hooks/useGetEncodedAddress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useApi } from '../contexts/ApiContext'
import { useCallback } from 'react'
import { encodesubstrateAddress } from '../utils/encodeSubstrateAddress'

export const useGetEncodedAddress = () => {
const { chainInfo } = useApi()

const getEncodedAddress = useCallback(
(address: string | Uint8Array | undefined) => {
if (!chainInfo || !address) {
return
}

return encodesubstrateAddress(address, chainInfo.ss58Format)
},
[chainInfo]
)

return getEncodedAddress
}
32 changes: 12 additions & 20 deletions packages/ui/src/pages/Creation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
import { styled } from '@mui/material/styles'
import { useApi } from '../../contexts/ApiContext'
import SignatorySelection from '../../components/select/SignatorySelection'
import { createKeyMulti, encodeAddress, sortAddresses } from '@polkadot/util-crypto'
import { createKeyMulti, sortAddresses } from '@polkadot/util-crypto'
import { useAccounts } from '../../contexts/AccountsContext'
import ThresholdSelection from './ThresholdSelection'
import NameSelection from './NameSelection'
Expand All @@ -17,6 +17,7 @@ import { useCheckBalance } from '../../hooks/useCheckBalance'
import { useMultisigProposalNeededFunds } from '../../hooks/useMultisigProposalNeededFunds'
import { usePureProxyCreationNeededFunds } from '../../hooks/usePureProxyCreationNeededFunds'
import { useGetSubscanLinks } from '../../hooks/useSubscanLink'
import { useGetEncodedAddress } from '../../hooks/useGetEncodedAddress'

interface Props {
className?: string
Expand All @@ -28,7 +29,7 @@ const MultisigCreation = ({ className }: Props) => {
const [signatories, setSignatories] = useState<string[]>([])
const [currentStep, setCurrentStep] = useState(0)
const isLastStep = useMemo(() => currentStep === steps.length - 1, [currentStep])
const { api, chainInfo } = useApi()
const { api } = useApi()
const [threshold, setThreshold] = useState<number | undefined>()
const { selectedSigner, selectedAccount, ownAddressList } = useAccounts()
const navigate = useNavigate()
Expand All @@ -45,24 +46,15 @@ const MultisigCreation = ({ className }: Props) => {
)
const [errorMessage, setErrorMessage] = useState('')
const { pureProxyCreationNeededFunds } = usePureProxyCreationNeededFunds()
const multiAddress = useMemo(() => {
if (!threshold) {
return
}

if (!chainInfo) {
return
}

const multiPubKey = createKeyMulti(signatories, threshold)
let res: string | undefined
try {
res = encodeAddress(multiPubKey, chainInfo.ss58Format)
} catch (e) {
console.error(`Error encoding the address ${multiPubKey}, skipping`, e)
}
return res
}, [chainInfo, signatories, threshold])
const multisigPubKey = useMemo(() => {
if (!threshold) return
return createKeyMulti(signatories, threshold)
}, [signatories, threshold])
const getEncodedAddress = useGetEncodedAddress()
const multiAddress = useMemo(
() => getEncodedAddress(multisigPubKey),
[getEncodedAddress, multisigPubKey]
)
const batchCall = useMemo(() => {
if (!api) {
// console.error('api is not ready')
Expand Down
9 changes: 2 additions & 7 deletions packages/ui/src/utils/encodeAccounts.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import { InjectedAccountWithMeta } from '@polkadot/extension-inject/types'
import { encodeAddress } from '@polkadot/util-crypto'
import { encodesubstrateAddress } from './encodeSubstrateAddress'

export const encodeAccounts = (
accounts: InjectedAccountWithMeta[] | string[],
ss58Format: number
) => {
return accounts
.map((account) => {
let encodedAddress: string | undefined
const addressToEncode = typeof account === 'string' ? account : account.address

try {
encodedAddress = encodeAddress(addressToEncode, ss58Format)
} catch (e) {
console.error(`Error encoding the address ${addressToEncode}, skipping`, e)
}
const encodedAddress = encodesubstrateAddress(addressToEncode, ss58Format)

if (typeof account === 'string') {
return encodedAddress
Expand Down
14 changes: 14 additions & 0 deletions packages/ui/src/utils/encodeSubstrateAddress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { encodeAddress } from '@polkadot/util-crypto'

export const encodesubstrateAddress = (address: string | Uint8Array, ss58Format: number) => {
if (typeof address === 'string' && address.startsWith('0x') && address.length === 42) {
console.log('invalid address format to encode', address)
return address
}

try {
return encodeAddress(address, ss58Format)
} catch (e) {
console.error(`Error encoding the address ${address}, skipping`, e)
}
}
25 changes: 25 additions & 0 deletions packages/ui/src/utils/getPubKeyFromAddress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { u8aToHex } from '@polkadot/util'
import { decodeAddress } from '@polkadot/util-crypto'
import { HexString } from '../types'

const decode = (address: string) => {
if (address.startsWith('0x')) {
return null
}

try {
return u8aToHex(decodeAddress(address))
} catch (e) {
console.error(`Error decoding the address ${address}, skipping`, e)
return null
}
}
export function getPubKeyFromAddress(address: string[]): HexString[]
export function getPubKeyFromAddress(address: string): HexString | null
export function getPubKeyFromAddress(address: string | string[]) {
if (Array.isArray(address)) {
return address.map(decode).filter((address) => !!address) as HexString[]
}

return decode(address)
}
18 changes: 6 additions & 12 deletions packages/ui/src/utils/namesUtil.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import { encodeAddress, decodeAddress } from '@polkadot/util-crypto'
import { u8aToHex } from '@polkadot/util'
import { AccountNames } from '../contexts/AccountNamesContext'
import { getPubKeyFromAddress } from './getPubKeyFromAddress'
import { encodesubstrateAddress } from './encodeSubstrateAddress'

export const encodeNames = (accounts: AccountNames, ss58Format: number) => {
const res = {} as AccountNames

Object.entries(accounts).forEach(([pubkey, name]) => {
let address: string | undefined
const address = encodesubstrateAddress(pubkey, ss58Format)

try {
address = encodeAddress(pubkey, ss58Format)
if (address) {
res[address] = name
} catch (e) {
console.error(`Error encoding the address ${address}, skipping`, e)
}
})
return res
Expand All @@ -22,12 +19,9 @@ export const decodeNames = (accounts: AccountNames) => {
const res = {} as AccountNames

Object.entries(accounts).forEach(([address, name]) => {
let pubkey: string | undefined
try {
pubkey = u8aToHex(decodeAddress(address))
const pubkey = getPubKeyFromAddress(address)
if (pubkey) {
res[pubkey] = name
} catch (e) {
console.error(`Error decoding the address ${address}, skipping`, e)
}
})
return res
Expand Down

0 comments on commit f929610

Please sign in to comment.