PHP WebShell

Текущая директория: /opt/BitGoJS/node_modules/web3-utils/src

Просмотр файла: formatter.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 { FormatterError } from 'web3-errors';
import { Bytes, DataFormat, FMT_BYTES, FMT_NUMBER, FormatType } from 'web3-types';
import { isNullish, isObject, JsonSchema, utils, ValidationSchemaInput } from 'web3-validator';
import { bytesToUint8Array, bytesToHex, numberToHex, toBigInt } from './converters.js';
import { mergeDeep } from './objects.js';
import { padLeft } from './string_manipulation.js';
import { isUint8Array, uint8ArrayConcat } from './uint8array.js';

const { parseBaseType } = utils;

export const isDataFormat = (dataFormat: unknown): dataFormat is DataFormat =>
	typeof dataFormat === 'object' &&
	!isNullish(dataFormat) &&
	'number' in dataFormat &&
	'bytes' in dataFormat;

/**
 * Finds the schema that corresponds to a specific data path within a larger JSON schema.
 * It works by iterating over the dataPath array and traversing the JSON schema one step at a time until it reaches the end of the path.
 *
 * @param schema - represents a JSON schema, which is an object that describes the structure of JSON data
 * @param dataPath - represents an array of strings that specifies the path to the data within the JSON schema
 * @param oneOfPath - represents an optional array of two-element tuples that specifies the "oneOf" option to choose, if the schema has oneOf and the data path can match multiple subschemas
 * @returns the JSON schema that matches the data path
 *
 */
const findSchemaByDataPath = (
	schema: JsonSchema,
	dataPath: string[],
	oneOfPath: [string, number][] = [],
): JsonSchema | undefined => {
	let result: JsonSchema = { ...schema } as JsonSchema;
	let previousDataPath: string | undefined;

	for (const dataPart of dataPath) {
		if (result.oneOf && previousDataPath) {
			const path = oneOfPath.find(function (element: [string, number]) {
				return (this as unknown as string) === element[0];
			}, previousDataPath ?? '');

			if (path && path[0] === previousDataPath) {
				// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
				result = result.oneOf[path[1]];
			}
		}
		if (!result.properties && !result.items) {
			return undefined;
		}

		if (result.properties) {
			result = (result.properties as Record<string, JsonSchema>)[dataPart];
		} else if (result.items && (result.items as JsonSchema).properties) {
			const node = (result.items as JsonSchema).properties as Record<string, JsonSchema>;

			if (!node) {
				return undefined;
			}

			result = node[dataPart];
		} else if (result.items && isObject(result.items)) {
			result = result.items;
		} else if (result.items && Array.isArray(result.items)) {
			result = result.items[parseInt(dataPart, 10)];
		}

		if (result && dataPart) previousDataPath = dataPart;
	}

	return result;
};
/**
 * Converts a value depending on the format
 * @param value - value to convert
 * @param ethType - The type of the value to be parsed
 * @param format - The format to be converted to
 * @returns - The value converted to the specified format
 */
export const convertScalarValue = (value: unknown, ethType: string, format: DataFormat) => {
	try {
		const { baseType, baseTypeSize } = parseBaseType(ethType);
		if (baseType === 'int' || baseType === 'uint') {
			switch (format.number) {
				case FMT_NUMBER.NUMBER:
					return Number(toBigInt(value));
				case FMT_NUMBER.HEX:
					return numberToHex(toBigInt(value));
				case FMT_NUMBER.STR:
					return toBigInt(value).toString();
				case FMT_NUMBER.BIGINT:
					return toBigInt(value);
				default:
					throw new FormatterError(`Invalid format: ${String(format.number)}`);
			}
		}
		if (baseType === 'bytes') {
			let paddedValue;
			if (baseTypeSize) {
				if (typeof value === 'string') paddedValue = padLeft(value, baseTypeSize * 2);
				else if (isUint8Array(value)) {
					paddedValue = uint8ArrayConcat(
						new Uint8Array(baseTypeSize - value.length),
						value,
					);
				}
			} else {
				paddedValue = value;
			}
			switch (format.bytes) {
				case FMT_BYTES.HEX:
					return bytesToHex(bytesToUint8Array(paddedValue as Bytes));
				case FMT_BYTES.UINT8ARRAY:
					return bytesToUint8Array(paddedValue as Bytes);
				default:
					throw new FormatterError(`Invalid format: ${String(format.bytes)}`);
			}
		}
	} catch (error) {
		// If someone didn't use `eth` keyword we can return original value
		// as the scope of this code is formatting not validation
		return value;
	}

	return value;
};
/**
 * Converts the data to the specified format
 * @param data - data to convert
 * @param schema - The JSON schema that describes the structure of the data
 * @param dataPath - A string array that specifies the path to the data within the JSON schema
 * @param format  - The format to be converted to
 * @param oneOfPath - An optional array of two-element tuples that specifies the "oneOf" option to choose, if the schema has oneOf and the data path can match multiple subschemas
 * @returns - The data converted to the specified format
 */
export const convert = (
	data: Record<string, unknown> | unknown[] | unknown,
	schema: JsonSchema,
	dataPath: string[],
	format: DataFormat,
	oneOfPath: [string, number][] = [],
) => {
	// If it's a scalar value
	if (!isObject(data) && !Array.isArray(data)) {
		return convertScalarValue(data, schema?.format as string, format);
	}

	const object = data as Record<string, unknown>;

	for (const [key, value] of Object.entries(object)) {
		dataPath.push(key);
		const schemaProp = findSchemaByDataPath(schema, dataPath, oneOfPath);

		// If value is a scaler value
		if (isNullish(schemaProp)) {
			delete object[key];
			dataPath.pop();

			continue;
		}

		// If value is an object, recurse into it
		if (isObject(value)) {
			convert(value, schema, dataPath, format);
			dataPath.pop();
			continue;
		}

		// If value is an array
		if (Array.isArray(value)) {
			let _schemaProp = schemaProp;

			// TODO This is a naive approach to solving the issue of
			// a schema using oneOf. This chunk of code was intended to handle
			// BlockSchema.transactions
			// TODO BlockSchema.transactions are not being formatted
			if (schemaProp?.oneOf !== undefined) {
				// The following code is basically saying:
				// if the schema specifies oneOf, then we are to loop
				// over each possible schema and check if they type of the schema
				// matches the type of value[0], and if so we use the oneOfSchemaProp
				// as the schema for formatting
				// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
				schemaProp.oneOf.forEach((oneOfSchemaProp: JsonSchema, index: number) => {
					if (
						!Array.isArray(schemaProp?.items) &&
						((typeof value[0] === 'object' &&
							(oneOfSchemaProp?.items as JsonSchema)?.type === 'object') ||
							(typeof value[0] === 'string' &&
								(oneOfSchemaProp?.items as JsonSchema)?.type !== 'object'))
					) {
						_schemaProp = oneOfSchemaProp;
						oneOfPath.push([key, index]);
					}
				});
			}

			if (isNullish(_schemaProp?.items)) {
				// Can not find schema for array item, delete that item
				delete object[key];
				dataPath.pop();

				continue;
			}

			// If schema for array items is a single type
			if (isObject(_schemaProp.items) && !isNullish(_schemaProp.items.format)) {
				for (let i = 0; i < value.length; i += 1) {
					(object[key] as unknown[])[i] = convertScalarValue(
						value[i],
						// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
						_schemaProp?.items?.format,
						format,
					);
				}

				dataPath.pop();
				continue;
			}

			// If schema for array items is an object
			if (!Array.isArray(_schemaProp?.items) && _schemaProp?.items?.type === 'object') {
				for (const arrObject of value) {
					convert(
						arrObject as Record<string, unknown> | unknown[],
						schema,
						dataPath,
						format,
						oneOfPath,
					);
				}

				dataPath.pop();
				continue;
			}

			// If schema for array is a tuple
			if (Array.isArray(_schemaProp?.items)) {
				for (let i = 0; i < value.length; i += 1) {
					(object[key] as unknown[])[i] = convertScalarValue(
						value[i],
						_schemaProp.items[i].format as string,
						format,
					);
				}

				dataPath.pop();
				continue;
			}
		}

		object[key] = convertScalarValue(value, schemaProp.format as string, format);

		dataPath.pop();
	}

	return object;
};

export const format = <
	DataType extends Record<string, unknown> | unknown[] | unknown,
	ReturnType extends DataFormat,
>(
	schema: ValidationSchemaInput | JsonSchema,
	data: DataType,
	returnFormat: ReturnType,
): FormatType<DataType, ReturnType> => {
	let dataToParse: Record<string, unknown> | unknown[] | unknown;

	if (isObject(data)) {
		dataToParse = mergeDeep({}, data);
	} else if (Array.isArray(data)) {
		dataToParse = [...data];
	} else {
		dataToParse = data;
	}

	// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
	const jsonSchema: JsonSchema = isObject(schema) ? schema : utils.ethAbiToJsonSchema(schema);

	if (!jsonSchema.properties && !jsonSchema.items && !jsonSchema.format) {
		throw new FormatterError('Invalid json schema for formatting');
	}

	return convert(dataToParse, jsonSchema, [], returnFormat) as FormatType<
		typeof data,
		ReturnType
	>;
};

Выполнить команду


Для локальной разработки. Не используйте в интернете!