Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/components/Args.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 <div style={{ wordBreak: 'break-all' }}>{u8aToString(bytes)}</div>;
}
} catch (_) {
// fall through to hex display
}
}

return <div style={{ wordBreak: 'break-all' }}>{value}</div>;
},
},
Expand Down
2 changes: 1 addition & 1 deletion src/components/Entries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
3 changes: 2 additions & 1 deletion src/packages/react-components/src/InputFile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
3 changes: 2 additions & 1 deletion src/packages/react-components/src/util/toAddress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/packages/react-hooks/src/useAccounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
3 changes: 2 additions & 1 deletion src/packages/react-hooks/src/useOwnStashInfos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
64 changes: 37 additions & 27 deletions src/packages/react-params/src/Param/BaseBytes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -22,6 +24,7 @@ interface Props {
isDisabled?: boolean;
isError?: boolean;
label?: React.ReactNode;
labelExtra?: React.ReactNode;
length?: number;
name?: string;
onChange?: RawParamOnChange;
Expand All @@ -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) {
Comment thread
carlhong marked this conversation as resolved.
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({
Expand All @@ -66,6 +75,7 @@ function BaseBytes({
isDisabled,
isError,
label,
labelExtra,
length = -1,
onChange,
onEnter,
Expand All @@ -77,27 +87,26 @@ function BaseBytes({
withLength,
}: Props): React.ReactElement<Props> {
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);
}
Comment thread
carlhong marked this conversation as resolved.

beValid = beValid && validate(val) && (length !== -1 ? val.length === length : val.length !== 0);
return undefined;
});
const [{ isValid }, setValidity] = useState<Validity>(() => ({
isAddress: false,
isValid: isHex(defaultValue) || isAscii(defaultValue),
}));
Comment thread
carlhong marked this conversation as resolved.

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 &&
Expand All @@ -106,7 +115,7 @@ function BaseBytes({
value: asHex ? u8aToHex(val) : val,
});

setIsValid(beValid);
setValidity({ isAddress, isValid: beValid, lastValue: val });
},
[asHex, length, onChange, validate, withLength]
);
Expand All @@ -115,11 +124,12 @@ function BaseBytes({
<Bare className={className}>
<Input
className={size}
defaultValue={defaultValue as string}
defaultValue={defaultValue}
isAction={!!children}
Comment thread
carlhong marked this conversation as resolved.
isDisabled={isDisabled}
isError={isError || !isValid}
label={label}
labelExtra={labelExtra}
onChange={_onChange}
onEnter={onEnter}
onEscape={onEscape}
Expand Down
2 changes: 1 addition & 1 deletion src/packages/react-params/src/Param/Hash256.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import React, { useCallback, useState } from 'react';
import { u8aToHex } from '@polkadot/util';
import { u8aToHexFixed as u8aToHex } from 'src/utils/helper/u8aToHex';
import type { Props } from '../types';

import { Toggle } from '../../../react-components/src';
Expand Down
3 changes: 2 additions & 1 deletion src/packages/react-params/src/Param/KeyValue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
// SPDX-License-Identifier: Apache-2.0

import React, { useCallback, useEffect, useState } from 'react';
import { compactAddLength, hexToU8a, u8aConcat } from '@polkadot/util';
import { compactAddLength, u8aConcat } from '@polkadot/util';
import { hexToU8aFixed as hexToU8a } from 'src/utils/helper/hexToU8a';
import type { Props } from '../types';

import { Input } from '../../../react-components/src';
Expand Down
3 changes: 2 additions & 1 deletion src/packages/react-params/src/Param/KeyValueArray.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

import type { Vec } from '@polkadot/types';
import type { KeyValue as Pair } from '@polkadot/types/interfaces';
import { assert, isHex, u8aToHex, u8aToString } from '@polkadot/util';
import { assert, isHex, u8aToString } from '@polkadot/util';
import { u8aToHexFixed as u8aToHex } from 'src/utils/helper/u8aToHex';
import React, { useCallback, useState } from 'react';
import { useTranslation } from '../translate';
import type { Props, RawParam } from '../types';
Expand Down
3 changes: 2 additions & 1 deletion src/packages/react-params/src/valueToText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import type { Codec } from '@polkadot/types/types';
import React from 'react';

import { Option, Raw } from '@polkadot/types';
import { isFunction, isNull, isUndefined, u8aToHex } from '@polkadot/util';
import { isFunction, isNull, isUndefined } from '@polkadot/util';
import { u8aToHexFixed as u8aToHex } from 'src/utils/helper/u8aToHex';

interface DivProps {
className?: string;
Expand Down
4 changes: 3 additions & 1 deletion src/utils/helper/address.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { TypeRegistry } from '@polkadot/types';
import { AccountId } from '@polkadot/types/interfaces';
import { hexToU8a, numberToU8a, stringToU8a, u8aToHex } from '@polkadot/util';
import { numberToU8a, stringToU8a } from '@polkadot/util';
import { decodeAddress, encodeAddress } from '@polkadot/util-crypto';
import { DONATE_ADDRESS } from 'src/config';
import { u8aToHexFixed as u8aToHex } from './u8aToHex';
import { hexToU8aFixed as hexToU8a } from './hexToU8a';

export const registry = new TypeRegistry();

Expand Down
34 changes: 34 additions & 0 deletions src/utils/helper/hexToU8a.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Fixed hexToU8a: https://github.com/polkadot-js/common/blob/master/packages/util/src/hex/toU8a.ts
// The @polkadot/util@8.x version has a float arithmetic bug for odd-length hex strings
// (e.g. '0x123' and '0x2300' both produce [0x23, 0x00]).
/* eslint-disable no-bitwise */
const CHR = '0123456789abcdef';
const HEX_U8 = new Array(256).fill(0);
const HEX_U16 = new Array(256 * 256).fill(0);

for (let i = 0; i < CHR.length; i++) {
HEX_U8[CHR.charCodeAt(i)] = i;
if (i > 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)];
Comment thread
carlhong marked this conversation as resolved.
}

return result;
}
2 changes: 1 addition & 1 deletion src/utils/helper/multisig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
47 changes: 47 additions & 0 deletions src/utils/helper/u8aToHex.ts
Original file line number Diff line number Diff line change
@@ -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);
}
3 changes: 2 additions & 1 deletion src/utils/helper/validate.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
Loading