Skip to content

Commit

Permalink
improve type checking for Uint8Array
Browse files Browse the repository at this point in the history
  • Loading branch information
Muhammad-Altabba committed Oct 9, 2023
1 parent 67251b5 commit 4b5d6cf
Show file tree
Hide file tree
Showing 9 changed files with 47 additions and 30 deletions.
4 changes: 2 additions & 2 deletions packages/web3-eth-abi/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/

import { AbiError } from 'web3-errors';
import { isNullish, leftPad, rightPad, toHex } from 'web3-utils';
import { isNullish, isUint8Array, leftPad, rightPad, toHex } from 'web3-utils';
import {
AbiInput,
AbiCoderStruct,
Expand Down Expand Up @@ -189,7 +189,7 @@ export const formatParam = (type: string, _param: unknown): unknown => {
// Format correct length for bytes[0-9]+
match = paramTypeBytes.exec(type);
if (match) {
const hexParam = param instanceof Uint8Array ? toHex(param) : param;
const hexParam = isUint8Array(param) ? toHex(param) : param;

Check warning on line 192 in packages/web3-eth-abi/src/utils.ts

View check run for this annotation

Codecov / codecov/patch

packages/web3-eth-abi/src/utils.ts#L192

Added line #L192 was not covered by tests

// format to correct length
const size = parseInt(match[1], 10);
Expand Down
16 changes: 7 additions & 9 deletions packages/web3-eth-accounts/src/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
bytesToHex,
fromUtf8,
hexToBytes,
isUint8Array,
numberToHex,
randomBytes,
sha3Raw,
Expand Down Expand Up @@ -82,10 +83,7 @@ export const parseAndValidatePrivateKey = (data: Bytes, ignoreLength?: boolean):
}

try {
privateKeyUint8Array =
data instanceof Uint8Array || data?.constructor?.name === 'Uint8Array'
? (data as Uint8Array)
: bytesToUint8Array(data);
privateKeyUint8Array = isUint8Array(data) ? (data ) : bytesToUint8Array(data);
} catch {
throw new InvalidPrivateKeyError();
}
Expand Down Expand Up @@ -344,7 +342,7 @@ export const recover = (
const V_INDEX = 130; // r = first 32 bytes, s = second 32 bytes, v = last byte of signature
const hashedMessage = prefixedOrR ? data : hashMessage(data);

let v = parseInt(signatureOrV.substring(V_INDEX),16); // 0x + r + s + v
let v = parseInt(signatureOrV.substring(V_INDEX), 16); // 0x + r + s + v
if (v > 26) {
v -= 27;
}
Expand All @@ -359,7 +357,7 @@ export const recover = (
const address = toChecksumAddress(`0x${publicHash.slice(-40)}`);

return address;
};
};;

/**
* Get the ethereum Address from a private key
Expand Down Expand Up @@ -393,15 +391,15 @@ export const privateKeyToAddress = (privateKey: Bytes): string => {
* Get the public key from a private key
*
* @param privateKey - String or Uint8Array of 32 bytes
* @param isCompressed - if true, will generate a 33 byte compressed public key instead of a 65 byte public key
* @param isCompressed - if true, will generate a 33 byte compressed public key instead of a 65 byte public key
* @returns The public key
* @example
* ```ts
* privateKeyToAddress("0x1e046a882bb38236b646c9f135cf90ad90a140810f439875f2a6dd8e50fa261f", true)
* > "0x42beb65f179720abaa3ec9a70a539629cbbc5ec65bb57e7fc78977796837e537662dd17042e6449dc843c281067a4d6d8d1a1775a13c41901670d5de7ee6503a" // uncompressed public key
* ```
*/
export const privateKeyToPublicKey = (privateKey: Bytes, isCompressed: boolean): string => {
export const privateKeyToPublicKey = (privateKey: Bytes, isCompressed: boolean): string => {
const privateKeyUint8Array = parseAndValidatePrivateKey(privateKey);

// Get public key from private key in compressed format
Expand Down Expand Up @@ -495,7 +493,7 @@ export const encrypt = async (
salt = randomBytes(32);
}

if (!(isString(password) || password instanceof Uint8Array)) {
if (!(isString(password) || isUint8Array(password))) {
throw new InvalidPasswordError();
}

Expand Down
8 changes: 6 additions & 2 deletions packages/web3-eth-accounts/src/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/
import { isHexPrefixed, isHexString } from 'web3-validator';
import { bytesToHex, hexToBytes, numberToHex } from 'web3-utils';
import { bytesToHex, hexToBytes, isUint8Array, numberToHex } from 'web3-utils';
import { secp256k1 } from '../tx/constants.js';
import { Hardfork } from './enums.js';
import { ToBytesInputTypes, TypeOutput, TypeOutputReturnType } from './types.js';
Expand Down Expand Up @@ -331,6 +331,10 @@ export const toUint8Array = function (v: ToBytesInputTypes): Uint8Array {
return v;
}

if (v?.constructor?.name === 'Uint8Array') {
return Uint8Array.from(v as unknown as Uint8Array);

Check warning on line 335 in packages/web3-eth-accounts/src/common/utils.ts

View check run for this annotation

Codecov / codecov/patch

packages/web3-eth-accounts/src/common/utils.ts#L335

Added line #L335 was not covered by tests
}

if (Array.isArray(v)) {
return Uint8Array.from(v);
}
Expand Down Expand Up @@ -420,7 +424,7 @@ const setLength = function (msg: Uint8Array, length: number, right: boolean) {
* @param {Uint8Array} input value to check
*/
export function assertIsUint8Array(input: unknown): asserts input is Uint8Array {
if (!(input instanceof Uint8Array)) {
if (!isUint8Array(input)) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
const msg = `This method only supports Uint8Array but input was: ${input}`;
throw new Error(msg);
Expand Down
5 changes: 3 additions & 2 deletions packages/web3-eth-accounts/src/tx/transactionFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/
import { isUint8Array } from 'web3-utils';
import { toUint8Array, uint8ArrayToBigInt } from '../common/utils.js';
import { FeeMarketEIP1559Transaction } from './eip1559Transaction.js';
import { AccessListEIP2930Transaction } from './eip2930Transaction.js';
Expand Down Expand Up @@ -104,8 +105,8 @@ export class TransactionFactory {
* @param txOptions - The transaction options
*/
public static fromBlockBodyData(data: Uint8Array | Uint8Array[], txOptions: TxOptions = {}) {
if (data instanceof Uint8Array || data?.constructor?.name === 'Uint8Array') {
return this.fromSerializedData(data as Uint8Array, txOptions);
if (isUint8Array(data)) {
return this.fromSerializedData(data , txOptions);
}
if (Array.isArray(data)) {
// It is a legacy transaction
Expand Down
7 changes: 4 additions & 3 deletions packages/web3-utils/src/converters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
InvalidNumberError,
InvalidUnitError,
} from 'web3-errors';
import { ensureIfUint8Array, isUint8Array } from './uint8array.js';

const base = BigInt(10);
const expo10 = (expo: number) => base ** BigInt(expo);
Expand Down Expand Up @@ -88,8 +89,8 @@ export type EtherUnits = keyof typeof ethUnitMap;
export const bytesToUint8Array = (data: Bytes): Uint8Array | never => {
validator.validate(['bytes'], [data]);

if (data instanceof Uint8Array || data?.constructor?.name === 'Uint8Array') {
return data as Uint8Array;
if (isUint8Array(data)) {
return data;
}

if (Array.isArray(data)) {
Expand Down Expand Up @@ -588,7 +589,7 @@ export const toChecksumAddress = (address: Address): string => {
// calling `Uint8Array.from` because `noble-hashes` checks with `instanceof Uint8Array` that fails in some edge cases:
// https://github.com/paulmillr/noble-hashes/issues/25#issuecomment-1750106284
const hash = utils.uint8ArrayToHexString(
keccak256(Uint8Array.from(utf8ToBytes(lowerCaseAddress))),
keccak256(ensureIfUint8Array(utf8ToBytes(lowerCaseAddress))),
);

if (
Expand Down
4 changes: 2 additions & 2 deletions packages/web3-utils/src/formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { isNullish, isObject, JsonSchema, utils, ValidationSchemaInput } from 'w
import { bytesToUint8Array, bytesToHex, numberToHex, toBigInt } from './converters.js';
import { mergeDeep } from './objects.js';
import { padLeft } from './string_manipulation.js';
import { uint8ArrayConcat } from './uint8array.js';
import { isUint8Array, uint8ArrayConcat } from './uint8array.js';

const { parseBaseType } = utils;

Expand Down Expand Up @@ -112,7 +112,7 @@ export const convertScalarValue = (value: unknown, ethType: string, format: Data
let paddedValue;
if (baseTypeSize) {
if (typeof value === 'string') paddedValue = padLeft(value, baseTypeSize * 2);
else if (value instanceof Uint8Array) {
else if (isUint8Array(value)) {
paddedValue = uint8ArrayConcat(
new Uint8Array(baseTypeSize - value.length),
value,
Expand Down
17 changes: 17 additions & 0 deletions packages/web3-utils/src/uint8array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,23 @@ GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/

export function isUint8Array(data: unknown | Uint8Array): data is Uint8Array {
return (
data instanceof Uint8Array ||
(data as { constructor: { name: string } })?.constructor?.name === 'Uint8Array'
);
}
export function ensureIfUint8Array<T = any>(data: T) {
if (
!(data instanceof Uint8Array) &&
(data as { constructor: { name: string } })?.constructor?.name === 'Uint8Array'

Check warning on line 27 in packages/web3-utils/src/uint8array.ts

View check run for this annotation

Codecov / codecov/patch

packages/web3-utils/src/uint8array.ts#L27

Added line #L27 was not covered by tests
) {
return Uint8Array.from(data as unknown as Uint8Array);

Check warning on line 29 in packages/web3-utils/src/uint8array.ts

View check run for this annotation

Codecov / codecov/patch

packages/web3-utils/src/uint8array.ts#L29

Added line #L29 was not covered by tests
}
return data;
}

export function uint8ArrayConcat(...parts: Uint8Array[]): Uint8Array {
const length = parts.reduce((prev, part) => {
const agg = prev + part.length;
Expand Down
5 changes: 3 additions & 2 deletions packages/web3-validator/src/validation/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { utf8ToBytes } from 'ethereum-cryptography/utils.js';
import { ValidInputTypes } from '../types.js';
import { uint8ArrayToHexString } from '../utils.js';
import { isHexStrict } from './string.js';
import { isUint8Array } from './bytes.js';

/**
* Checks the checksum of a given address. Will also return false on non-checksum addresses.
Expand Down Expand Up @@ -47,13 +48,13 @@ export const checkAddressCheckSum = (data: string): boolean => {
* Checks if a given string is a valid Ethereum address. It will also check the checksum, if the address has upper and lowercase letters.
*/
export const isAddress = (value: ValidInputTypes, checkChecksum = true) => {
if (typeof value !== 'string' && !(value instanceof Uint8Array)) {
if (typeof value !== 'string' && !isUint8Array(value)) {
return false;
}

let valueToCheck: string;

if (value instanceof Uint8Array) {
if (isUint8Array(value)) {
valueToCheck = uint8ArrayToHexString(value);
} else if (typeof value === 'string' && !isHexStrict(value)) {
valueToCheck = value.toLowerCase().startsWith('0x') ? value : `0x${value}`;
Expand Down
11 changes: 3 additions & 8 deletions packages/web3-validator/src/validation/bytes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { isHexStrict } from './string.js';
/**
* checks input if typeof data is valid Uint8Array input
*/
export const isUint8Array = (data: ValidInputTypes) =>
export const isUint8Array = (data: ValidInputTypes): data is Uint8Array =>
data instanceof Uint8Array || data?.constructor?.name === 'Uint8Array';

export const isBytes = (
Expand All @@ -31,12 +31,7 @@ export const isBytes = (
abiType: 'bytes',
},
) => {
if (
typeof value !== 'string' &&
!Array.isArray(value) &&
!(value instanceof Uint8Array) &&
value?.constructor?.name !== 'Uint8Array'
) {
if (typeof value !== 'string' && !Array.isArray(value) && !isUint8Array(value)) {
return false;
}

Expand All @@ -63,7 +58,7 @@ export const isBytes = (
}
valueToCheck = new Uint8Array(value);
} else {
valueToCheck = value as Uint8Array;
valueToCheck = value ;
}

if (options?.abiType) {
Expand Down

0 comments on commit 4b5d6cf

Please sign in to comment.