PHP WebShell
Текущая директория: /opt/BitGoJS/node_modules/web3-validator/src
Просмотр файла: utils.ts
/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 { InvalidBytesError, InvalidNumberError } from 'web3-errors';
import { VALID_ETH_BASE_TYPES } from './constants.js';
import {
FullValidationSchema,
JsonSchema,
ShortValidationSchema,
ValidationSchemaInput,
ValidInputTypes,
} from './types.js';
import { isAbiParameterSchema } from './validation/abi.js';
import { isHexStrict } from './validation/string.js';
import { Web3ValidatorError } from './errors.js';
const extraTypes = ['hex', 'number', 'blockNumber', 'blockNumberOrTag', 'filter', 'bloom'];
export const parseBaseType = <T = typeof VALID_ETH_BASE_TYPES[number]>(
type: string,
): {
baseType?: T;
baseTypeSize: number | undefined;
arraySizes: number[];
isArray: boolean;
} => {
// Remove all empty spaces to avoid any parsing issue.
let strippedType = type.replace(/ /, '');
let baseTypeSize: number | undefined;
let isArray = false;
let arraySizes: number[] = [];
if (type.includes('[')) {
// Extract the array type
strippedType = strippedType.slice(0, strippedType.indexOf('['));
// Extract array indexes
arraySizes = [...type.matchAll(/(?:\[(\d*)\])/g)]
.map(match => parseInt(match[1], 10))
.map(size => (Number.isNaN(size) ? -1 : size));
isArray = arraySizes.length > 0;
}
if (VALID_ETH_BASE_TYPES.includes(strippedType)) {
return { baseType: strippedType as unknown as T, isArray, baseTypeSize, arraySizes };
}
if (strippedType.startsWith('int')) {
baseTypeSize = parseInt(strippedType.substring(3), 10);
strippedType = 'int';
} else if (strippedType.startsWith('uint')) {
baseTypeSize = parseInt(type.substring(4), 10);
strippedType = 'uint';
} else if (strippedType.startsWith('bytes')) {
baseTypeSize = parseInt(strippedType.substring(5), 10);
strippedType = 'bytes';
} else {
return { baseType: undefined, isArray: false, baseTypeSize: undefined, arraySizes };
}
return { baseType: strippedType as unknown as T, isArray, baseTypeSize, arraySizes };
};
const convertEthType = (
type: string,
parentSchema: JsonSchema = {},
): { format?: string; required?: boolean } => {
const typePropertyPresent = Object.keys(parentSchema).includes('type');
if (typePropertyPresent) {
throw new Web3ValidatorError([
{
keyword: 'eth',
message: 'Either "eth" or "type" can be presented in schema',
params: { eth: type },
instancePath: '',
schemaPath: '',
},
]);
}
const { baseType, baseTypeSize } = parseBaseType(type);
if (!baseType && !extraTypes.includes(type)) {
throw new Web3ValidatorError([
{
keyword: 'eth',
message: `Eth data type "${type}" is not valid`,
params: { eth: type },
instancePath: '',
schemaPath: '',
},
]);
}
if (baseType) {
if (baseType === 'tuple') {
throw new Error('"tuple" type is not implemented directly.');
}
return { format: `${baseType}${baseTypeSize ?? ''}`, required: true };
}
if (type) {
return { format: type, required: true };
}
return {};
};
export const abiSchemaToJsonSchema = (
abis: ShortValidationSchema | FullValidationSchema,
level = '/0',
) => {
const schema: JsonSchema = {
type: 'array',
items: [],
maxItems: abis.length,
minItems: abis.length,
};
for (const [index, abi] of abis.entries()) {
// eslint-disable-next-line no-nested-ternary
let abiType!: string;
let abiName!: string;
let abiComponents: ShortValidationSchema | FullValidationSchema | undefined = [];
// If it's a complete Abi Parameter
// e.g. {name: 'a', type: 'uint'}
if (isAbiParameterSchema(abi)) {
abiType = abi.type;
abiName = abi.name || `${level}/${index}`;
abiComponents = abi.components as FullValidationSchema;
// If its short form string value e.g. ['uint']
} else if (typeof abi === 'string') {
abiType = abi;
abiName = `${level}/${index}`;
// If it's provided in short form of tuple e.g. [['uint', 'string']]
} else if (Array.isArray(abi)) {
// If its custom tuple e.g. ['tuple[2]', ['uint', 'string']]
if (
abi[0] &&
typeof abi[0] === 'string' &&
abi[0].startsWith('tuple') &&
!Array.isArray(abi[0]) &&
abi[1] &&
Array.isArray(abi[1])
) {
// eslint-disable-next-line prefer-destructuring
abiType = abi[0];
abiName = `${level}/${index}`;
abiComponents = abi[1] as ReadonlyArray<ShortValidationSchema>;
} else {
abiType = 'tuple';
abiName = `${level}/${index}`;
abiComponents = abi;
}
}
const { baseType, isArray, arraySizes } = parseBaseType(abiType);
let childSchema: JsonSchema;
let lastSchema = schema;
for (let i = arraySizes.length - 1; i > 0; i -= 1) {
childSchema = {
type: 'array',
$id: abiName,
items: [],
maxItems: arraySizes[i],
minItems: arraySizes[i],
};
if (arraySizes[i] < 0) {
delete childSchema.maxItems;
delete childSchema.minItems;
}
// lastSchema.items is a Schema, concat with 'childSchema'
if (!Array.isArray(lastSchema.items)) {
lastSchema.items = [lastSchema.items as JsonSchema, childSchema];
} // lastSchema.items is an empty Scheme array, set it to 'childSchema'
else if (lastSchema.items.length === 0) {
lastSchema.items = [childSchema];
} // lastSchema.items is a non-empty Scheme array, append 'childSchema'
else {
lastSchema.items.push(childSchema);
}
lastSchema = childSchema;
}
if (baseType === 'tuple' && !isArray) {
const nestedTuple = abiSchemaToJsonSchema(abiComponents, abiName);
nestedTuple.$id = abiName;
(lastSchema.items as JsonSchema[]).push(nestedTuple);
} else if (baseType === 'tuple' && isArray) {
const arraySize = arraySizes[0];
const item: JsonSchema = {
type: 'array',
$id: abiName,
items: abiSchemaToJsonSchema(abiComponents, abiName),
...(arraySize >= 0 && { minItems: arraySize, maxItems: arraySize }),
};
(lastSchema.items as JsonSchema[]).push(item);
} else if (isArray) {
const arraySize = arraySizes[0];
const item: JsonSchema = {
type: 'array',
$id: abiName,
items: convertEthType(abiType),
...(arraySize >= 0 && { minItems: arraySize, maxItems: arraySize }),
};
(lastSchema.items as JsonSchema[]).push(item);
} else if (Array.isArray(lastSchema.items)) {
// Array of non-tuple items
lastSchema.items.push({ $id: abiName, ...convertEthType(abiType) });
} else {
// Nested object
(lastSchema.items as JsonSchema[]).push({
$id: abiName,
...convertEthType(abiType),
});
}
lastSchema = schema;
}
return schema;
};
export const ethAbiToJsonSchema = (abis: ValidationSchemaInput) => abiSchemaToJsonSchema(abis);
export const fetchArrayElement = (data: Array<unknown>, level: number): unknown => {
if (level === 1) {
return data;
}
return fetchArrayElement(data[0] as Array<unknown>, level - 1);
};
export const transformJsonDataToAbiFormat = (
abis: FullValidationSchema,
data: ReadonlyArray<unknown> | Record<string, unknown>,
transformedData?: Array<unknown>,
): Array<unknown> => {
const newData: Array<unknown> = [];
for (const [index, abi] of abis.entries()) {
// eslint-disable-next-line no-nested-ternary
let abiType!: string;
let abiName!: string;
let abiComponents: ShortValidationSchema | FullValidationSchema | undefined = [];
// If it's a complete Abi Parameter
// e.g. {name: 'a', type: 'uint'}
if (isAbiParameterSchema(abi)) {
abiType = abi.type;
abiName = abi.name;
abiComponents = abi.components as FullValidationSchema;
// If its short form string value e.g. ['uint']
} else if (typeof abi === 'string') {
abiType = abi;
// If it's provided in short form of tuple e.g. [['uint', 'string']]
} else if (Array.isArray(abi)) {
// If its custom tuple e.g. ['tuple[2]', ['uint', 'string']]
if (abi[1] && Array.isArray(abi[1])) {
abiType = abi[0] as string;
abiComponents = abi[1] as ReadonlyArray<ShortValidationSchema>;
} else {
abiType = 'tuple';
abiComponents = abi;
}
}
const { baseType, isArray, arraySizes } = parseBaseType(abiType);
const dataItem = Array.isArray(data)
? (data as Array<unknown>)[index]
: (data as Record<string, unknown>)[abiName];
if (baseType === 'tuple' && !isArray) {
newData.push(
transformJsonDataToAbiFormat(
abiComponents as FullValidationSchema,
dataItem as Array<unknown>,
transformedData,
),
);
} else if (baseType === 'tuple' && isArray) {
const tupleData = [];
for (const tupleItem of dataItem as Array<unknown>) {
// Nested array
if (arraySizes.length > 1) {
const nestedItems = fetchArrayElement(
tupleItem as Array<unknown>,
arraySizes.length - 1,
);
const nestedData = [];
for (const nestedItem of nestedItems as Array<unknown>) {
nestedData.push(
transformJsonDataToAbiFormat(
abiComponents as FullValidationSchema,
nestedItem as Array<unknown>,
transformedData,
),
);
}
tupleData.push(nestedData);
} else {
tupleData.push(
transformJsonDataToAbiFormat(
abiComponents as FullValidationSchema,
tupleItem as Array<unknown>,
transformedData,
),
);
}
}
newData.push(tupleData);
} else {
newData.push(dataItem);
}
}
// Have to reassign before pushing to transformedData
// eslint-disable-next-line no-param-reassign
transformedData = transformedData ?? [];
transformedData.push(...newData);
return transformedData;
};
/**
* Code points to int
*/
export const codePointToInt = (codePoint: number): number => {
if (codePoint >= 48 && codePoint <= 57) {
/* ['0'..'9'] -> [0..9] */
return codePoint - 48;
}
if (codePoint >= 65 && codePoint <= 70) {
/* ['A'..'F'] -> [10..15] */
return codePoint - 55;
}
if (codePoint >= 97 && codePoint <= 102) {
/* ['a'..'f'] -> [10..15] */
return codePoint - 87;
}
throw new Error(`Invalid code point: ${codePoint}`);
};
/**
* Converts value to it's number representation
*/
export const hexToNumber = (value: string): bigint | number => {
if (!isHexStrict(value)) {
throw new Error('Invalid hex string');
}
const [negative, hexValue] = value.startsWith('-') ? [true, value.slice(1)] : [false, value];
const num = BigInt(hexValue);
if (num > Number.MAX_SAFE_INTEGER) {
return negative ? -num : num;
}
if (num < Number.MIN_SAFE_INTEGER) {
return num;
}
return negative ? -1 * Number(num) : Number(num);
};
/**
* Converts value to it's hex representation
*/
export const numberToHex = (value: ValidInputTypes): string => {
if ((typeof value === 'number' || typeof value === 'bigint') && value < 0) {
return `-0x${value.toString(16).slice(1)}`;
}
if ((typeof value === 'number' || typeof value === 'bigint') && value >= 0) {
return `0x${value.toString(16)}`;
}
if (typeof value === 'string' && isHexStrict(value)) {
const [negative, hex] = value.startsWith('-') ? [true, value.slice(1)] : [false, value];
const hexValue = hex.split(/^(-)?0(x|X)/).slice(-1)[0];
return `${negative ? '-' : ''}0x${hexValue.replace(/^0+/, '').toLowerCase()}`;
}
if (typeof value === 'string' && !isHexStrict(value)) {
return numberToHex(BigInt(value));
}
throw new InvalidNumberError(value);
};
/**
* Adds a padding on the left of a string, if value is a integer or bigInt will be converted to a hex string.
*/
export const padLeft = (value: ValidInputTypes, characterAmount: number, sign = '0'): string => {
if (typeof value === 'string' && !isHexStrict(value)) {
return value.padStart(characterAmount, sign);
}
const hex = typeof value === 'string' && isHexStrict(value) ? value : numberToHex(value);
const [prefix, hexValue] = hex.startsWith('-') ? ['-0x', hex.slice(3)] : ['0x', hex.slice(2)];
return `${prefix}${hexValue.padStart(characterAmount, sign)}`;
};
export function uint8ArrayToHexString(uint8Array: Uint8Array): string {
let hexString = '0x';
for (const e of uint8Array) {
const hex = e.toString(16);
hexString += hex.length === 1 ? `0${hex}` : hex;
}
return hexString;
}
// for optimized technique for hex to bytes conversion
const charCodeMap = {
zero: 48,
nine: 57,
A: 65,
F: 70,
a: 97,
f: 102,
} as const
function charCodeToBase16(char: number) {
if (char >= charCodeMap.zero && char <= charCodeMap.nine)
return char - charCodeMap.zero
if (char >= charCodeMap.A && char <= charCodeMap.F)
return char - (charCodeMap.A - 10)
if (char >= charCodeMap.a && char <= charCodeMap.f)
return char - (charCodeMap.a - 10)
return undefined
}
export function hexToUint8Array(hex: string): Uint8Array {
let offset = 0;
if (hex.startsWith('0') && (hex[1] === 'x' || hex[1] === 'X')) {
offset = 2;
}
if (hex.length % 2 !== 0) {
throw new InvalidBytesError(`hex string has odd length: ${hex}`);
}
const length = (hex.length - offset) / 2;
const bytes = new Uint8Array(length);
for (let index = 0, j = offset; index < length; index+=1) {
// eslint-disable-next-line no-plusplus
const nibbleLeft = charCodeToBase16(hex.charCodeAt(j++))
// eslint-disable-next-line no-plusplus
const nibbleRight = charCodeToBase16(hex.charCodeAt(j++))
if (nibbleLeft === undefined || nibbleRight === undefined) {
throw new InvalidBytesError(
`Invalid byte sequence ("${hex[j - 2]}${
hex[j - 1]
}" in "${hex}").`,
)
}
bytes[index] = nibbleLeft * 16 + nibbleRight
}
return bytes
}
// @TODO: Remove this function and its usages once all sub dependencies uses version 1.3.3 or above of @noble/hashes
export function ensureIfUint8Array<T = any>(data: T) {
if (
!(data instanceof Uint8Array) &&
(data as { constructor: { name: string } })?.constructor?.name === 'Uint8Array'
) {
return Uint8Array.from(data as unknown as Uint8Array);
}
return data;
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!