Skip to content

Commit

Permalink
fix 2d array bug of abiSchemaToJsonSchema in web3-validator (#6836)
Browse files Browse the repository at this point in the history
* fix 2d array bug of abiSchemaToJsonSchema in web3-validator

* fix 2d array bug of abiSchemaToJsonSchema in web3-validator(add comments)

* fix abi json schema test case:
- 123 cannot be uint
- test case("nested tuple object in nested array") has a wrong data structure of tuple[][3]

* fix function abiSchemaToJsonSchema in iterating over arraySizes array to each dimension

* FIX:
1. fix function convertToZod to distinguish tuple and array, add checks in maxItems & minItems
2. revert abiSchemaToJsonSchema of utils and fix
3. add testcases in validator.validate

* UPDATE: contract test

---------

Co-authored-by: Yi Zhang <yi.y.zhang@maplequad.com>
  • Loading branch information
EtlesL and Yi Zhang authored Mar 20, 2024
1 parent f943944 commit 1f81ff0
Show file tree
Hide file tree
Showing 6 changed files with 504 additions and 415 deletions.
2 changes: 1 addition & 1 deletion packages/web3-eth-contract/test/unit/contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,7 @@ describe('Contract', () => {
expect(error).toBeInstanceOf(Web3ValidatorError);
// eslint-disable-next-line jest/no-conditional-expect
expect((error as Web3ValidatorError).message).toBe(
'Web3 validator found 1 error[s]:\nmust NOT have more than 1 items',
'Web3 validator found 2 error[s]:\nmust NOT have more than 1 items\nvalue "true" at "/1" must pass "string" validation',
);
}

Expand Down
6 changes: 5 additions & 1 deletion packages/web3-validator/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,8 @@ Documentation:

- Fixed an issue with detecting Uint8Array (#6486)

## [Unreleased]
## [Unreleased]

### Fixed

- Multi-dimensional arrays(with a fix length) are now handled properly when parsing ABIs (#6798)
55 changes: 22 additions & 33 deletions packages/web3-validator/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ export const abiSchemaToJsonSchema = (
for (let i = arraySizes.length - 1; i > 0; i -= 1) {
childSchema = {
type: 'array',
$id: abiName,
items: [],
maxItems: arraySizes[i],
minItems: arraySizes[i],
Expand All @@ -192,7 +193,7 @@ export const abiSchemaToJsonSchema = (
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 = [childSchema];
} // lastSchema.items is a non-empty Scheme array, append 'childSchema'
else {
lastSchema.items.push(childSchema);
Expand All @@ -205,43 +206,31 @@ export const abiSchemaToJsonSchema = (
nestedTuple.$id = abiName;
(lastSchema.items as JsonSchema[]).push(nestedTuple);
} else if (baseType === 'tuple' && isArray) {
const arraySize = arraySizes[0];
const item: JsonSchema = {
$id: abiName,
type: 'array',
items: abiSchemaToJsonSchema(abiComponents, abiName),
maxItems: arraySize,
minItems: arraySize,
};

if (arraySize < 0) {
delete item.maxItems;
delete item.minItems;
}

(lastSchema.items as JsonSchema[]).push(item);
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(String(baseType)),
minItems: arraySize,
maxItems: arraySize,
};

if (arraySize < 0) {
delete item.maxItems;
delete item.minItems;
}

(lastSchema.items as JsonSchema[]).push(item);
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).items as JsonSchema[]).push({
(lastSchema.items as JsonSchema[]).push({
$id: abiName,
...convertEthType(abiType),
});
Expand Down Expand Up @@ -505,4 +494,4 @@ export function ensureIfUint8Array<T = any>(data: T) {
return Uint8Array.from(data as unknown as Uint8Array);
}
return data;
}
}
11 changes: 9 additions & 2 deletions packages/web3-validator/src/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ const convertToZod = (schema: JsonSchema): ZodType => {
}

if (schema?.type === 'array' && schema?.items) {
if (Array.isArray(schema.items) && schema.items.length > 0) {
if (Array.isArray(schema.items) && schema.items.length > 1
&& schema.maxItems !== undefined
&& new Set(schema.items.map((item: JsonSchema) => item.$id)).size === schema.items.length) {
const arr: Partial<[ZodTypeAny, ...ZodTypeAny[]]> = [];
for (const item of schema.items) {
const zItem = convertToZod(item);
Expand All @@ -54,7 +56,12 @@ const convertToZod = (schema: JsonSchema): ZodType => {
}
return z.tuple(arr as [ZodTypeAny, ...ZodTypeAny[]]);
}
return z.array(convertToZod(schema.items as JsonSchema));
const nextSchema = Array.isArray(schema.items) ? schema.items[0] : schema.items;
let zodArraySchema = z.array(convertToZod(nextSchema));

zodArraySchema = schema.minItems !== undefined ? zodArraySchema.min(schema.minItems) : zodArraySchema;
zodArraySchema = schema.maxItems !== undefined ? zodArraySchema.max(schema.maxItems) : zodArraySchema;
return zodArraySchema;
}

if (schema.oneOf && Array.isArray(schema.oneOf)) {
Expand Down
Loading

1 comment on commit 1f81ff0

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: 1f81ff0 Previous: 6c075db Ratio
processingTx 9621 ops/sec (±3.59%) 9301 ops/sec (±4.81%) 0.97
processingContractDeploy 41138 ops/sec (±6.53%) 39129 ops/sec (±7.62%) 0.95
processingContractMethodSend 20161 ops/sec (±6.65%) 19443 ops/sec (±5.19%) 0.96
processingContractMethodCall 40476 ops/sec (±5.92%) 38971 ops/sec (±6.34%) 0.96
abiEncode 44693 ops/sec (±6.40%) 44252 ops/sec (±6.92%) 0.99
abiDecode 30015 ops/sec (±7.77%) 30419 ops/sec (±8.89%) 1.01
sign 1587 ops/sec (±3.24%) 1656 ops/sec (±4.08%) 1.04
verify 382 ops/sec (±0.40%) 373 ops/sec (±0.78%) 0.98

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.