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
>;
};
Выполнить команду
Для локальной разработки. Не используйте в интернете!