Files
pezkuwi-apps/packages/page-utilities/src/Convert.tsx
T

134 lines
3.5 KiB
TypeScript

// Copyright 2017-2026 @pezkuwi/app-utilities authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { HexString } from '@pezkuwi/util/types';
import React, { useCallback, useMemo, useState } from 'react';
import { createOption } from '@pezkuwi/app-settings/util';
import { createSs58 } from '@pezkuwi/apps-config';
import { allNetworks } from '@pezkuwi/networks';
import { Dropdown, InputAddressSimple, Static } from '@pezkuwi/react-components';
import { useApi } from '@pezkuwi/react-hooks';
import { formatNumber, u8aToHex } from '@pezkuwi/util';
import { base58Decode, checkAddressChecksum, decodeAddress, encodeAddress, isAddress } from '@pezkuwi/util-crypto';
import { useTranslation } from './translate.js';
interface Props {
className?: string;
}
interface State {
inputSS58: number;
publicKey: HexString | null;
}
function getState (input: string | null): State {
try {
if (input && isAddress(input)) {
const decoded = base58Decode(input);
const [,,, inputSS58] = checkAddressChecksum(decoded);
const publicU8a = decodeAddress(input);
return {
inputSS58,
publicKey: u8aToHex(publicU8a)
};
}
} catch {
// ignore
}
return {
inputSS58: 42,
publicKey: null
};
}
function Addresses ({ className }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { chainSS58 } = useApi();
const [{ inputSS58, publicKey }, setState] = useState<State>({ inputSS58: 42, publicKey: null });
const [prefix, setPrefix] = useState(-1);
const setAddress = useCallback(
(address: string | null) =>
setState(getState(address)),
[]
);
const prefixOptions = useMemo(
() => {
const network = allNetworks.find(({ prefix }) => prefix === chainSS58);
return createSs58(t).map((o) =>
createOption(o, ['default'], 'empty', (o.value === -1
? network
? ` (${network.displayName}, ${chainSS58 || 0})`
: ` (${chainSS58 || 0})`
: ` (${o.value})`
))
);
},
[chainSS58, t]
);
const converted = useMemo(
() => publicKey && encodeAddress(publicKey, prefix === -1 ? chainSS58 : prefix),
[chainSS58, prefix, publicKey]
);
return (
<div className={className}>
<div className='ui--row'>
<InputAddressSimple
autoFocus
isError={!publicKey}
label={t('address to convert')}
noConvert
onChange={setAddress}
/>
</div>
<div className='ui--row'>
<Dropdown
defaultValue={prefix}
label={t('address prefix')}
onChange={setPrefix}
options={prefixOptions}
/>
</div>
{publicKey && (
<>
<div className='ui--row'>
<Static
className='medium'
label={t('input ss58 prefix')}
value={formatNumber(inputSS58)}
/>
</div>
{converted && (
<div className='ui--row'>
<InputAddressSimple
defaultValue={converted}
isDisabled
key={converted}
label={t('converted address')}
/>
</div>
)}
<div className='ui--row'>
<Static
className='full'
label={t('hex public key')}
value={publicKey}
/>
</div>
</>
)}
</div>
);
}
export default React.memo(Addresses);