diff --git a/src/components/Args.tsx b/src/components/Args.tsx
index 81ca11e6..1e6a8c62 100644
--- a/src/components/Args.tsx
+++ b/src/components/Args.tsx
@@ -4,11 +4,13 @@ import { ChainProperties } from '@polkadot/types/interfaces';
import { encodeAddress } from '@polkadot/util-crypto';
import { Table } from 'antd';
import { ColumnsType } from 'antd/lib/table';
+import { isAscii, isHex, u8aToString } from '@polkadot/util';
import { isArray, isObject, isString, toString } from 'lodash';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useApi } from '../hooks';
import { Chain } from '../providers/api-provider';
+import { hexToU8aFixed } from '../utils/helper/hexToU8a';
import {
formatBalance,
isAddressType,
@@ -140,6 +142,17 @@ export function Args({ args, className, section, method }: ArgsProps) {
return value;
}
+ if (isString(value) && isHex(value)) {
+ try {
+ const bytes = hexToU8aFixed(value);
+ if (bytes.length > 0 && isAscii(bytes)) {
+ return
{u8aToString(bytes)}
;
+ }
+ } catch (_) {
+ // fall through to hex display
+ }
+ }
+
return {value}
;
},
},
diff --git a/src/components/Entries.tsx b/src/components/Entries.tsx
index c76cad26..eb9691f1 100644
--- a/src/components/Entries.tsx
+++ b/src/components/Entries.tsx
@@ -54,7 +54,7 @@ function MemberStatus(props: { entry: Entry; pair: KeyringJson; isInProgress: bo
const { entry, isInProgress } = props;
const { address } = props.pair;
const { approvals } = props.entry;
- const approved = approvals.includes(address);
+ const approved = approvals?.includes(address) ?? false;
const { networkConfig } = useApi();
const { constants } = useDataSourceTools(networkConfig);
diff --git a/src/packages/react-components/src/InputFile.tsx b/src/packages/react-components/src/InputFile.tsx
index cb5e55bf..4eb2e729 100644
--- a/src/packages/react-components/src/InputFile.tsx
+++ b/src/packages/react-components/src/InputFile.tsx
@@ -8,7 +8,8 @@ import React, { createRef, useCallback, useState } from 'react';
import Dropzone, { DropzoneRef } from 'react-dropzone';
import styled from 'styled-components';
-import { formatNumber, hexToU8a, isHex, u8aToString } from '@polkadot/util';
+import { formatNumber, isHex, u8aToString } from '@polkadot/util';
+import { hexToU8aFixed as hexToU8a } from 'src/utils/helper/hexToU8a';
import Labelled from './Labelled';
import { useTranslation } from './translate';
diff --git a/src/packages/react-components/src/util/toAddress.ts b/src/packages/react-components/src/util/toAddress.ts
index 49692d12..ef328b89 100644
--- a/src/packages/react-components/src/util/toAddress.ts
+++ b/src/packages/react-components/src/util/toAddress.ts
@@ -3,7 +3,8 @@
// SPDX-License-Identifier: Apache-2.0
import { keyring } from '@polkadot/ui-keyring';
-import { assert, hexToU8a, isHex } from '@polkadot/util';
+import { assert, isHex } from '@polkadot/util';
+import { hexToU8aFixed as hexToU8a } from 'src/utils/helper/hexToU8a';
import { ethereumEncode } from '@polkadot/util-crypto';
// eslint-disable-next-line complexity
diff --git a/src/packages/react-hooks/src/useAccounts.ts b/src/packages/react-hooks/src/useAccounts.ts
index 673d981f..7621df45 100644
--- a/src/packages/react-hooks/src/useAccounts.ts
+++ b/src/packages/react-hooks/src/useAccounts.ts
@@ -4,7 +4,7 @@
import { useEffect, useState } from 'react';
import { keyring } from '@polkadot/ui-keyring';
-import { u8aToHex } from '@polkadot/util';
+import { u8aToHexFixed as u8aToHex } from 'src/utils/helper/u8aToHex';
import { decodeAddress } from '@polkadot/util-crypto';
import { useIsMountedRef } from './useIsMountedRef';
diff --git a/src/packages/react-hooks/src/useOwnStashInfos.ts b/src/packages/react-hooks/src/useOwnStashInfos.ts
index 105aa036..ac786d24 100644
--- a/src/packages/react-hooks/src/useOwnStashInfos.ts
+++ b/src/packages/react-hooks/src/useOwnStashInfos.ts
@@ -6,7 +6,8 @@
import type { DeriveStakingAccount } from '@polkadot/api-derive/types';
import type { AccountId, ValidatorPrefs } from '@polkadot/types/interfaces';
import type { Codec, ITuple } from '@polkadot/types/types';
-import { u8aConcat, u8aToHex } from '@polkadot/util';
+import { u8aConcat } from '@polkadot/util';
+import { u8aToHexFixed as u8aToHex } from 'src/utils/helper/u8aToHex';
import { useEffect, useMemo, useState } from 'react';
import type { StakerState } from './types';
import { useAccounts } from './useAccounts';
diff --git a/src/packages/react-params/src/Param/BaseBytes.tsx b/src/packages/react-params/src/Param/BaseBytes.tsx
index 3189f6b5..cb22b2f2 100644
--- a/src/packages/react-params/src/Param/BaseBytes.tsx
+++ b/src/packages/react-params/src/Param/BaseBytes.tsx
@@ -6,7 +6,9 @@ import type { TypeDef } from '@polkadot/types/types';
import React, { useCallback, useState } from 'react';
-import { compactAddLength, hexToU8a, isAscii, isHex, isU8a, stringToU8a, u8aToHex, u8aToString } from '@polkadot/util';
+import { compactAddLength, isAscii, isHex, stringToU8a, u8aToString, u8aToU8a } from '@polkadot/util';
+import { u8aToHexFixed as u8aToHex } from 'src/utils/helper/u8aToHex';
+import { hexToU8aFixed as hexToU8a } from 'src/utils/helper/hexToU8a';
import { decodeAddress } from '@polkadot/util-crypto';
import { CopyButton, Input } from '../../../react-components/src';
import type { RawParam, RawParamOnChange, RawParamOnEnter, RawParamOnEscape, Size } from '../types';
@@ -22,6 +24,7 @@ interface Props {
isDisabled?: boolean;
isError?: boolean;
label?: React.ReactNode;
+ labelExtra?: React.ReactNode;
length?: number;
name?: string;
onChange?: RawParamOnChange;
@@ -35,27 +38,33 @@ interface Props {
withLength?: boolean;
}
+interface Validity {
+ isAddress: boolean;
+ isValid: boolean;
+ lastValue?: Uint8Array;
+}
+
const defaultValidate = (): boolean => true;
-function convertInput(value: string): [boolean, Uint8Array] {
+function convertInput(value: string): [boolean, boolean, Uint8Array] {
if (value === '0x') {
- return [true, new Uint8Array([])];
+ return [true, false, new Uint8Array([])];
} else if (value.startsWith('0x')) {
try {
- return [true, hexToU8a(value)];
+ return [true, false, isHex(value) ? hexToU8a(value) : stringToU8a(value)];
} catch (error) {
- return [false, new Uint8Array([])];
+ return [false, false, new Uint8Array([])];
}
}
// maybe it is an ss58?
try {
- return [true, decodeAddress(value)];
+ return [true, true, decodeAddress(value)];
} catch (error) {
// we continue
}
- return isAscii(value) ? [true, stringToU8a(value)] : [value === '0x', new Uint8Array([])];
+ return isAscii(value) ? [true, false, stringToU8a(value)] : [value === '0x', false, new Uint8Array([])];
}
function BaseBytes({
@@ -66,6 +75,7 @@ function BaseBytes({
isDisabled,
isError,
label,
+ labelExtra,
length = -1,
onChange,
onEnter,
@@ -77,27 +87,26 @@ function BaseBytes({
withLength,
}: Props): React.ReactElement {
const { t } = useTranslation();
- const [defaultValue] = useState(
- value
- ? isDisabled && isU8a(value) && isAscii(value)
- ? u8aToString(value)
- : isHex(value)
- ? value
- : // eslint-disable-next-line no-magic-numbers
- u8aToHex(value as Uint8Array, isDisabled ? 256 : -1)
- : undefined
- );
- const [isValid, setIsValid] = useState(false);
+ const [defaultValue] = useState((): string | undefined => {
+ if (value) {
+ const u8a = u8aToU8a(value as Uint8Array);
- const _onChange = useCallback(
- (hex: string): void => {
- let [beValid, val] = convertInput(hex);
+ return isAscii(u8a) ? u8aToString(u8a) : u8aToHex(u8a);
+ }
- beValid = beValid && validate(val) && (length !== -1 ? val.length === length : val.length !== 0);
+ return undefined;
+ });
+ const [{ isValid }, setValidity] = useState(() => ({
+ isAddress: false,
+ isValid: isHex(defaultValue) || isAscii(defaultValue),
+ }));
- if (withLength && beValid) {
- val = compactAddLength(val);
- }
+ const _onChange = useCallback(
+ (hex: string): void => {
+ const [convertedValid, isAddress, u8a] = convertInput(hex);
+ const beValid =
+ convertedValid && validate(u8a) && (length !== -1 ? u8a.length === length : u8a.length !== 0 || hex === '0x');
+ const val = withLength && beValid ? compactAddLength(u8a) : u8a;
// eslint-disable-next-line
onChange &&
@@ -106,7 +115,7 @@ function BaseBytes({
value: asHex ? u8aToHex(val) : val,
});
- setIsValid(beValid);
+ setValidity({ isAddress, isValid: beValid, lastValue: val });
},
[asHex, length, onChange, validate, withLength]
);
@@ -115,11 +124,12 @@ function BaseBytes({
9) {
+ HEX_U8[CHR.toUpperCase().charCodeAt(i)] = i;
+ }
+}
+for (let i = 0; i < 256; i++) {
+ const s = i << 8;
+ for (let j = 0; j < 256; j++) {
+ HEX_U16[s | j] = (HEX_U8[i] << 4) | HEX_U8[j];
+ }
+}
+
+export function hexToU8aFixed(value: string, bitLength = -1): Uint8Array {
+ let s = value.startsWith('0x') ? 2 : 0;
+ const decLength = Math.ceil((value.length - s) / 2);
+ const endLength = Math.ceil(bitLength === -1 ? decLength : bitLength / 8);
+ const result = new Uint8Array(endLength);
+ const offset = endLength > decLength ? endLength - decLength : 0;
+
+ for (let i = offset; i < endLength; i++, s += 2) {
+ result[i] = HEX_U16[(value.charCodeAt(s) << 8) | value.charCodeAt(s + 1)];
+ }
+
+ return result;
+}
diff --git a/src/utils/helper/multisig.ts b/src/utils/helper/multisig.ts
index e9f988ce..0d33a27d 100644
--- a/src/utils/helper/multisig.ts
+++ b/src/utils/helper/multisig.ts
@@ -2,10 +2,10 @@ import { ApiPromise } from '@polkadot/api';
import { Call } from '@polkadot/types/interfaces';
import keyring from '@polkadot/ui-keyring';
import { KeyringAddress } from '@polkadot/ui-keyring/types';
-import { u8aToHex } from '@polkadot/util';
import { createKeyMulti } from '@polkadot/util-crypto';
import store from 'store';
import { Network, ShareScope, WalletFormValue } from '../../model';
+import { u8aToHexFixed as u8aToHex } from './u8aToHex';
interface MultiInfo {
isMultisig: boolean;
diff --git a/src/utils/helper/u8aToHex.ts b/src/utils/helper/u8aToHex.ts
new file mode 100644
index 00000000..9b7ad6a5
--- /dev/null
+++ b/src/utils/helper/u8aToHex.ts
@@ -0,0 +1,47 @@
+/* eslint-disable complexity */
+// Fixed u8aToHex: https://github.com/polkadot-js/common/blob/master/packages/util/src/u8a/toHex.ts
+// The @polkadot/util@8.x version uses DataView which has alignment issues;
+// the new version uses direct array indexing with pre-computed lookup tables.
+/* eslint-disable no-bitwise */
+const U8: string[] = new Array(256);
+const U16: string[] = new Array(256 * 256);
+
+for (let n = 0; n < 256; n++) {
+ U8[n] = n.toString(16).padStart(2, '0');
+}
+for (let i = 0; i < 256; i++) {
+ const s = i << 8;
+ for (let j = 0; j < 256; j++) {
+ U16[s | j] = U8[i] + U8[j];
+ }
+}
+
+function hex(value: Uint8Array, result: string): string {
+ const mod = value.length % 2 | 0;
+ const length = (value.length - mod) | 0;
+
+ for (let i = 0; i < length; i += 2) {
+ result += U16[(value[i] << 8) | value[i + 1]];
+ }
+ if (mod) {
+ result += U8[value[length] | 0];
+ }
+
+ return result;
+}
+
+export function u8aToHexFixed(value?: Uint8Array | null, bitLength = -1, isPrefixed = true): string {
+ const empty = isPrefixed ? '0x' : '';
+
+ if (!value || !value.length) {
+ return empty;
+ } else if (bitLength > 0) {
+ const length = Math.ceil(bitLength / 8);
+
+ if (value.length > length) {
+ return `${hex(value.subarray(0, length / 2), empty)}\u2026${hex(value.subarray(value.length - length / 2), '')}`;
+ }
+ }
+
+ return hex(value, empty);
+}
diff --git a/src/utils/helper/validate.ts b/src/utils/helper/validate.ts
index 8cd3879f..ff3d2384 100644
--- a/src/utils/helper/validate.ts
+++ b/src/utils/helper/validate.ts
@@ -1,5 +1,6 @@
import { decodeAddress, encodeAddress } from '@polkadot/keyring';
-import { hexToU8a, isHex } from '@polkadot/util';
+import { isHex } from '@polkadot/util';
+import { hexToU8aFixed as hexToU8a } from './hexToU8a';
export const isSS58Address = (address: string) => {
try {