diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f9695c527f..081fb17b6c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2650,6 +2650,12 @@ If there are any bugs, improvements, optimizations or any new feature proposal f ## [Unreleased] +### Fixed + +#### web3-utils + +- Fixed format schema with `oneOf` doesn't work correctly (#7055) + ### Added #### web3-eth-accounts diff --git a/packages/web3-utils/src/formatter.ts b/packages/web3-utils/src/formatter.ts index 665965a8eed..a25c69c26a4 100644 --- a/packages/web3-utils/src/formatter.ts +++ b/packages/web3-utils/src/formatter.ts @@ -131,6 +131,11 @@ export const convertScalarValue = (value: unknown, ethType: string, format: Data throw new FormatterError(`Invalid format: ${String(format.bytes)}`); } } + + if (baseType === 'string') { + return String(value); + } + } catch (error) { // If someone didn't use `eth` keyword we can return original value // as the scope of this code is formatting not validation @@ -289,7 +294,7 @@ export const convert = ( } else { for (const [key, value] of Object.entries(object)) { dataPath.push(key); - const schemaProp = findSchemaByDataPath(schema, dataPath, oneOfPath); + let schemaProp = findSchemaByDataPath(schema, dataPath, oneOfPath); // If value is a scaler value if (isNullish(schemaProp)) { @@ -322,6 +327,20 @@ export const convert = ( continue; } + // 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 specifies format + // 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 + if ((schemaProp?.format === undefined) && (schemaProp?.oneOf !== undefined)) { + for (const [_index, oneOfSchemaProp] of schemaProp.oneOf.entries()) { + if ((oneOfSchemaProp?.format !== undefined)) { + schemaProp = oneOfSchemaProp; + break; + } + }; + } + object[key] = convertScalarValue(value, schemaProp.format as string, format); dataPath.pop(); diff --git a/packages/web3-utils/test/unit/formatter.test.ts b/packages/web3-utils/test/unit/formatter.test.ts index aa1a14fb6c9..9f2bbfc3f1b 100644 --- a/packages/web3-utils/test/unit/formatter.test.ts +++ b/packages/web3-utils/test/unit/formatter.test.ts @@ -479,6 +479,17 @@ describe('formatter', () => { ).toEqual(new Uint8Array([16, 11, 202])); }); }); + + describe('string', () => { + it('should format string for 123', () => { + expect( + format({ format: 'string' }, 123, { + number: FMT_NUMBER.STR, + bytes: FMT_BYTES.HEX, + }), + ).toBe('123'); + }); + }); }); describe('array values', () => { @@ -827,6 +838,56 @@ describe('formatter', () => { expect(result).toEqual(expected); }); + + it('should format object with oneOf', () => { + const schema = { + type: 'object', + properties: { + from: { + format: 'address', + }, + to: { + oneOf: [{ format: 'string' }, { type: 'null' }], + }, + }, + }; + + const data ={ + from: '0x7ed0e85b8e1e925600b4373e6d108f34ab38a401', + to: 123, + } + ; + + const result = { from: '0x7ed0e85b8e1e925600b4373e6d108f34ab38a401', to: '123' }; + + expect( + format(schema, data, { number: FMT_NUMBER.HEX, bytes: FMT_BYTES.HEX }), + ).toEqual(result); + }); + + it('should format object with oneOf when property is undefined', () => { + const schema = { + type: 'object', + properties: { + from: { + format: 'address', + }, + to: { + oneOf: [{ format: 'string' }, { type: 'null' }], + }, + }, + }; + + const data ={ + from: '0x7ed0e85b8e1e925600b4373e6d108f34ab38a401' + }; + + const result = { from: '0x7ed0e85b8e1e925600b4373e6d108f34ab38a401'}; + + expect( + format(schema, data, { number: FMT_NUMBER.HEX, bytes: FMT_BYTES.HEX }), + ).toEqual(result); + }); }); describe('isDataFormat', () => { describe('valid cases', () => {