Skip to content

Commit

Permalink
fix(protocol-parser): properly decode empty records (#1177)
Browse files Browse the repository at this point in the history
Co-authored-by: alvarius <[email protected]>
  • Loading branch information
holic and alvrs authored Jul 19, 2023
1 parent f03531d commit 4bb7e8c
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/itchy-kids-relax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@latticexyz/protocol-parser": patch
---

`decodeRecord` now properly decodes empty records
5 changes: 5 additions & 0 deletions packages/protocol-parser/src/decodeKeyTuple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import { Schema } from "./common";
// key tuples are encoded in the same way as abi.encode, so we can decode them with viem

export function decodeKeyTuple(keySchema: Schema, keyTuple: readonly Hex[]): StaticPrimitiveType[] {
if (keySchema.staticFields.length !== keyTuple.length) {
throw new Error(
`key tuple length ${keyTuple.length} does not match key schema length ${keySchema.staticFields.length}`
);
}
return keyTuple.map(
(key, index) => decodeAbiParameters([{ type: keySchema.staticFields[index] }], key)[0] as StaticPrimitiveType
);
Expand Down
11 changes: 11 additions & 0 deletions packages/protocol-parser/src/decodeRecord.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,15 @@ describe("decodeRecord", () => {
);
expect(values).toStrictEqual([1, 2n, [3, 4], "some string"]);
});

it("can decode an empty record", () => {
const schema = { staticFields: [], dynamicFields: ["string", "string"] } as const;
const values = decodeRecord(schema, "0x0000000000000000000000000000000000000000000000000000000000000000");
expect(values).toMatchInlineSnapshot(`
[
"",
"",
]
`);
});
});
17 changes: 13 additions & 4 deletions packages/protocol-parser/src/decodeRecord.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { StaticPrimitiveType, DynamicPrimitiveType, staticAbiTypeToByteLength } from "@latticexyz/schema-type";
import {
StaticPrimitiveType,
DynamicPrimitiveType,
staticAbiTypeToByteLength,
dynamicAbiTypeToDefaultValue,
} from "@latticexyz/schema-type";
import { Hex, sliceHex } from "viem";
import { Schema } from "./common";
import { decodeDynamicField } from "./decodeDynamicField";
Expand Down Expand Up @@ -37,9 +42,13 @@ export function decodeRecord(schema: Schema, data: Hex): readonly (StaticPrimiti

schema.dynamicFields.forEach((fieldType, i) => {
const dataLength = dataLayout.fieldByteLengths[i];
const value = decodeDynamicField(fieldType, sliceHex(data, bytesOffset, bytesOffset + dataLength));
bytesOffset += dataLength;
values.push(value);
if (dataLength > 0) {
const value = decodeDynamicField(fieldType, sliceHex(data, bytesOffset, bytesOffset + dataLength));
bytesOffset += dataLength;
values.push(value);
} else {
values.push(dynamicAbiTypeToDefaultValue[fieldType]);
}
});

// Warn user if dynamic data length doesn't match the schema, because data corruption might be possible.
Expand Down
16 changes: 16 additions & 0 deletions packages/protocol-parser/src/hexToSchema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,22 @@ describe("hexToSchema", () => {
],
}
`);

expect(hexToSchema("0x00570800616100030700001f0000000000000000000000000000000000000000")).toMatchInlineSnapshot(`
{
"dynamicFields": [],
"staticFields": [
"address",
"address",
"uint8",
"uint32",
"uint64",
"uint8",
"uint8",
"uint256",
],
}
`);
});

it("throws if schema hex data is not bytes32", () => {
Expand Down
3 changes: 3 additions & 0 deletions packages/protocol-parser/src/hexToSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ export function hexToSchema(data: Hex): Schema {
// validate static data length
const actualStaticDataLength = staticFields.reduce((acc, fieldType) => acc + staticAbiTypeToByteLength[fieldType], 0);
if (actualStaticDataLength !== staticDataLength) {
console.warn(
`Schema "${data}" static data length (${staticDataLength}) did not match the summed length of all static fields (${actualStaticDataLength}). Is \`staticAbiTypeToByteLength\` up to date with Solidity schema types?`
);
throw new SchemaStaticLengthMismatchError(data, staticDataLength, actualStaticDataLength);
}

Expand Down

0 comments on commit 4bb7e8c

Please sign in to comment.