Skip to content

Commit 2adabe3

Browse files
committed
improve type check, increase test coverage
1 parent 569591a commit 2adabe3

File tree

3 files changed

+58
-13
lines changed

3 files changed

+58
-13
lines changed

packages/core/src/build/factory.test.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ test("buildLogFactory handles Morpho CreateMarket marketParams.oracle", () => {
7373
const testEventAbiItem = parseAbiItem([
7474
"struct SomeNestedStruct { uint256 c1; address[3] c2; }",
7575
"struct SomeStruct { address b1; SomeNestedStruct[42] b2; }",
76-
"event SomeEvent(SomeStruct indexed a1, SomeStruct a2, address[] a3, (uint256[] x, address y) z, (string s, address t)[10] u)",
76+
"event SomeEvent(SomeStruct indexed a1, SomeStruct a2, address[] a3, (uint256[] x, address y) z, (string s, address t)[10] u, uint256 indexed v, address indexed w)",
7777
]);
7878

7979
test("buildLogFactory handles fixed length arrays and tuples", () => {
@@ -91,11 +91,27 @@ test("buildLogFactory handles fixed length arrays and tuples", () => {
9191
});
9292
});
9393

94+
test("buildLogFactory handles indexed address parameters", () => {
95+
const criteria = buildLogFactory({
96+
address: "0xa",
97+
event: testEventAbiItem,
98+
parameterPath: "w",
99+
chainId: 1,
100+
});
101+
102+
expect(criteria).toMatchObject({
103+
address: "0xa",
104+
eventSelector: getEventSelector(testEventAbiItem),
105+
childAddressLocation: "topic3",
106+
});
107+
});
108+
94109
test("buildLogFactory throws if provided path is nested in an indexed parameter", () => {
95110
expect(() => {
96111
buildLogFactory({
97112
address: "0xa",
98113
event: testEventAbiItem,
114+
// @ts-expect-error
99115
parameterPath: "a1.b2[10].c2[1]",
100116
chainId: 1,
101117
});
@@ -143,6 +159,18 @@ test("buildLogFactory throws if provided path is not an address", () => {
143159
}).toThrowError("Factory event parameter is not an address. Got 'uint256'.");
144160
});
145161

162+
test("buildLogFactory throws if provided path is not an address (indexed)", () => {
163+
expect(() => {
164+
buildLogFactory({
165+
address: "0xa",
166+
event: testEventAbiItem,
167+
// @ts-expect-error
168+
parameterPath: "v",
169+
chainId: 1,
170+
});
171+
}).toThrowError("Factory event parameter is not an address. Got 'uint256'.");
172+
});
173+
146174
test("buildLogFactory throws if provided path is not in a static type", () => {
147175
expect(() => {
148176
buildLogFactory({

packages/core/src/build/factory.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,31 +30,40 @@ export function buildLogFactory<event extends AbiEvent>({
3030
convertToDotNotation(parameterPath).split(".");
3131

3232
// Check if the provided parameter is present in the list of indexed inputs.
33-
const indexedInputPosition = event.inputs
34-
.filter((x) => "indexed" in x && x.indexed)
35-
.findIndex((input) => input.name === parameter);
33+
const indexedInputPosition = event.inputs.findIndex(
34+
(input) => input.indexed && input.name === parameter,
35+
);
3636

3737
if (indexedInputPosition > -1) {
38-
// If the parameter is indexed, nested paths cannot be accessed.
38+
// If the parameter is indexed, nested paths cannot be accessed (reference types are hashed).
3939
if (pathSegments.length > 0) {
4040
throw new Error(
4141
`Factory event parameter is indexed, so nested path '${parameterPath}' cannot be accessed.`,
4242
);
4343
}
4444

45+
// Type must be address
46+
if (event.inputs[indexedInputPosition]!.type !== "address") {
47+
throw new Error(
48+
`Factory event parameter is not an address. Got '${event.inputs[indexedInputPosition]!.type}'.`,
49+
);
50+
}
51+
52+
const topicPosition = event.inputs
53+
.filter((input) => input.indexed)
54+
.findIndex((input) => input.name === parameter);
55+
4556
return {
4657
type: "log",
4758
chainId,
4859
address,
4960
eventSelector,
5061
// Add 1 because inputs will not contain an element for topic0 (the signature).
51-
childAddressLocation: `topic${(indexedInputPosition + 1) as 1 | 2 | 3}`,
62+
childAddressLocation: `topic${(topicPosition + 1) as 1 | 2 | 3}`,
5263
};
5364
}
5465

55-
const nonIndexedInputs = event.inputs.filter(
56-
(x) => !("indexed" in x && x.indexed),
57-
);
66+
const nonIndexedInputs = event.inputs.filter((input) => !input.indexed);
5867
const nonIndexedInputPosition = nonIndexedInputs.findIndex(
5968
(input) => input.name === parameter,
6069
);

packages/core/src/config/address.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import type { SolidityArrayWithTuple, SolidityTuple } from "abitype";
1+
import type {
2+
AbiEventParameter,
3+
SolidityArrayWithTuple,
4+
SolidityTuple,
5+
} from "abitype";
26
import type { AbiEvent, AbiParameter } from "viem";
37

48
type CommonFactoryParams<event extends AbiEvent = AbiEvent> = {
@@ -21,6 +25,7 @@ export type DeprecatedFactoryEventParameter<event extends AbiEvent = AbiEvent> =
2125
type ExtractValidPaths<T extends AbiParameter> = T extends {
2226
name: infer Name extends string;
2327
type: infer Type;
28+
indexed?: false;
2429
}
2530
? Type extends SolidityTuple
2631
? // Case 1: Tuple types - must access nested fields with dot notation
@@ -39,7 +44,7 @@ type ExtractValidPaths<T extends AbiParameter> = T extends {
3944
? `${Name}[${number}].${ExtractValidPaths<Components[number]>}`
4045
: never
4146
: Type extends `address[${number}]`
42-
? // Case 3: Simple arrays - must access with index
47+
? // Case 3: Fixed length address arrays - must access with index
4348
// Example: "myArray[0]"
4449
`${Name}[${number}]`
4550
: // Case 4: Primitive types - only address type can be accessed directly
@@ -51,8 +56,11 @@ type ExtractValidPaths<T extends AbiParameter> = T extends {
5156
type FactoryEventParameter<event extends AbiEvent = AbiEvent> = {
5257
/** Path to the field in factory event parameters that contains the new child contract address. */
5358
parameterPath: event["inputs"][number] extends infer Input extends
54-
AbiParameter
55-
? ExtractValidPaths<Input>
59+
AbiEventParameter
60+
? // Only allow indexed parameter if address type
61+
Input extends { indexed: true; type: "address"; name: infer Name }
62+
? Name
63+
: ExtractValidPaths<Input>
5664
: never;
5765
parameter?: never;
5866
};

0 commit comments

Comments
 (0)