@apibara/indexer 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/starknet/felt.d.ts +8 -0
- package/dist/starknet/felt.js +14 -1
- package/dist/starknet/felt.js.map +1 -1
- package/dist/starknet/felt.test.js +4 -2
- package/dist/starknet/felt.test.js.map +1 -1
- package/dist/starknet/index.d.ts +1 -0
- package/dist/starknet/index.js +1 -0
- package/dist/starknet/index.js.map +1 -1
- package/dist/starknet/parser.d.ts +7 -3
- package/dist/starknet/parser.js +24 -8
- package/dist/starknet/parser.js.map +1 -1
- package/dist/starknet/parser.test.js +26 -8
- package/dist/starknet/parser.test.js.map +1 -1
- package/package.json +1 -1
- package/src/starknet/felt.test.ts +8 -2
- package/src/starknet/felt.ts +15 -1
- package/src/starknet/index.ts +1 -0
- package/src/starknet/parser.test.ts +34 -8
- package/src/starknet/parser.ts +29 -10
package/dist/starknet/felt.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
export type FieldElement = `0x${string}`;
|
|
3
3
|
export declare const FieldElement: z.ZodEffects<z.ZodString, `0x${string}`, string>;
|
|
4
|
+
/**
|
|
5
|
+
* Compute the selector from a function or event name.
|
|
6
|
+
*
|
|
7
|
+
* The return value is padded to 32 bytes.
|
|
8
|
+
* @param name the function/event name
|
|
9
|
+
* @returns a field element
|
|
10
|
+
*/
|
|
11
|
+
export declare function getSelector(name: string): FieldElement;
|
package/dist/starknet/felt.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { hash } from "starknet";
|
|
2
3
|
export const FieldElement = z.string().transform((value, ctx) => {
|
|
3
4
|
const regex = /^0x[0-9a-fA-F]{1,64}$/;
|
|
4
5
|
if (!regex.test(value)) {
|
|
@@ -7,6 +8,18 @@ export const FieldElement = z.string().transform((value, ctx) => {
|
|
|
7
8
|
message: `FieldElement must be a hex string with a 0x prefix, got ${value}`,
|
|
8
9
|
});
|
|
9
10
|
}
|
|
10
|
-
|
|
11
|
+
const bn = BigInt(value);
|
|
12
|
+
const padded = bn.toString(16).padStart(64, "0");
|
|
13
|
+
return `0x${padded}`;
|
|
11
14
|
});
|
|
15
|
+
/**
|
|
16
|
+
* Compute the selector from a function or event name.
|
|
17
|
+
*
|
|
18
|
+
* The return value is padded to 32 bytes.
|
|
19
|
+
* @param name the function/event name
|
|
20
|
+
* @returns a field element
|
|
21
|
+
*/
|
|
22
|
+
export function getSelector(name) {
|
|
23
|
+
return FieldElement.parse(hash.getSelectorFromName(name));
|
|
24
|
+
}
|
|
12
25
|
//# sourceMappingURL=felt.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"felt.js","sourceRoot":"","sources":["../../src/starknet/felt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"felt.js","sourceRoot":"","sources":["../../src/starknet/felt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAIhC,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;IAC9D,MAAM,KAAK,GAAG,uBAAuB,CAAC;IAEtC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;QACtB,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,CAAC,CAAC,YAAY,CAAC,MAAM;YAC3B,OAAO,EAAE,2DAA2D,KAAK,EAAE;SAC5E,CAAC,CAAC;KACJ;IAED,MAAM,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACzB,MAAM,MAAM,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACjD,OAAO,KAAK,MAAM,EAAkB,CAAC;AACvC,CAAC,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5D,CAAC"}
|
|
@@ -2,10 +2,12 @@ import { describe, expect, test } from "vitest";
|
|
|
2
2
|
import { FieldElement } from "./felt";
|
|
3
3
|
describe("FieldElement", () => {
|
|
4
4
|
test("parse address", () => {
|
|
5
|
-
FieldElement.parse("0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7");
|
|
5
|
+
const output = FieldElement.parse("0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7");
|
|
6
|
+
expect(output).toEqual("0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7");
|
|
6
7
|
});
|
|
7
8
|
test("parse value", () => {
|
|
8
|
-
FieldElement.parse("0x1");
|
|
9
|
+
const output = FieldElement.parse("0x1");
|
|
10
|
+
expect(output).toEqual("0x0000000000000000000000000000000000000000000000000000000000000001");
|
|
9
11
|
});
|
|
10
12
|
test("parse invalid value", () => {
|
|
11
13
|
expect(() => FieldElement.parse("0x")).toThrowError();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"felt.test.js","sourceRoot":"","sources":["../../src/starknet/felt.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE;QACzB,YAAY,CAAC,KAAK,
|
|
1
|
+
{"version":3,"file":"felt.test.js","sourceRoot":"","sources":["../../src/starknet/felt.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE;QACzB,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAC/B,oEAAoE,CACrE,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CACpB,oEAAoE,CACrE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,aAAa,EAAE,GAAG,EAAE;QACvB,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACzC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CACpB,oEAAoE,CACrE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC/B,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC;IACxD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/starknet/index.d.ts
CHANGED
package/dist/starknet/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/starknet/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/starknet/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
import { type Abi } from "starknet";
|
|
1
2
|
import { FieldElement } from "./felt";
|
|
2
3
|
import { EventFilter } from "./filter";
|
|
3
|
-
import { type Abi } from "starknet";
|
|
4
4
|
/** Build a stream filter from a contract ABI. */
|
|
5
5
|
export declare class Contract {
|
|
6
|
-
readonly
|
|
7
|
-
readonly
|
|
6
|
+
readonly address: FieldElement;
|
|
7
|
+
readonly abi: Abi;
|
|
8
|
+
private selectorToEvent;
|
|
8
9
|
constructor(contractAddress: FieldElement, contractAbi: Abi);
|
|
9
10
|
private _findEvent;
|
|
11
|
+
private _populateSelectorToEvent;
|
|
10
12
|
/** Filter events based on their name from the contract's ABI. */
|
|
11
13
|
eventFilter(name: string, opts?: Pick<EventFilter, "includeReceipt" | "includeTransaction" | "includeReverted">): EventFilter;
|
|
14
|
+
/** Returns the event name based on its selector. */
|
|
15
|
+
lookupEventFromSelector(selector: FieldElement): string | undefined;
|
|
12
16
|
}
|
package/dist/starknet/parser.js
CHANGED
|
@@ -1,18 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getSelector } from "./felt";
|
|
2
2
|
/** Build a stream filter from a contract ABI. */
|
|
3
3
|
export class Contract {
|
|
4
4
|
// Read-only properties for the contract's address and its ABI.
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
address;
|
|
6
|
+
abi;
|
|
7
|
+
selectorToEvent;
|
|
7
8
|
constructor(contractAddress, contractAbi) {
|
|
8
|
-
this.
|
|
9
|
-
this.
|
|
9
|
+
this.address = contractAddress;
|
|
10
|
+
this.abi = contractAbi;
|
|
11
|
+
this.selectorToEvent = {};
|
|
12
|
+
this._populateSelectorToEvent();
|
|
10
13
|
}
|
|
11
14
|
_findEvent(name) {
|
|
12
15
|
// Find the event in the ABI matching the provided name.
|
|
13
|
-
const event = this.
|
|
16
|
+
const event = this.abi.find((item) => item.type === "event" && item.name === name);
|
|
14
17
|
return event;
|
|
15
18
|
}
|
|
19
|
+
_populateSelectorToEvent() {
|
|
20
|
+
this.selectorToEvent = {};
|
|
21
|
+
for (const event of this.abi) {
|
|
22
|
+
if (event.type !== "event") {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
this.selectorToEvent[getSelector(event.name)] = event.name;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
16
28
|
/** Filter events based on their name from the contract's ABI. */
|
|
17
29
|
eventFilter(name, opts = {}) {
|
|
18
30
|
const event = this._findEvent(name);
|
|
@@ -22,12 +34,16 @@ export class Contract {
|
|
|
22
34
|
}
|
|
23
35
|
// Return the found event.
|
|
24
36
|
return {
|
|
25
|
-
fromAddress: this.
|
|
26
|
-
keys: [
|
|
37
|
+
fromAddress: this.address,
|
|
38
|
+
keys: [getSelector(event.name)],
|
|
27
39
|
includeTransaction: opts.includeTransaction ?? false,
|
|
28
40
|
includeReceipt: opts.includeReceipt ?? false,
|
|
29
41
|
includeReverted: opts.includeReverted ?? false,
|
|
30
42
|
};
|
|
31
43
|
}
|
|
44
|
+
/** Returns the event name based on its selector. */
|
|
45
|
+
lookupEventFromSelector(selector) {
|
|
46
|
+
return this.selectorToEvent[selector];
|
|
47
|
+
}
|
|
32
48
|
}
|
|
33
49
|
//# sourceMappingURL=parser.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/starknet/parser.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/starknet/parser.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,WAAW,EAAE,MAAM,QAAQ,CAAC;AAGnD,iDAAiD;AACjD,MAAM,OAAO,QAAQ;IACnB,+DAA+D;IACtD,OAAO,CAAe;IACtB,GAAG,CAAM;IAEV,eAAe,CAA+B;IAEtD,YAAY,eAA6B,EAAE,WAAgB;QACzD,IAAI,CAAC,OAAO,GAAG,eAAe,CAAC;QAC/B,IAAI,CAAC,GAAG,GAAG,WAAW,CAAC;QACvB,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC;QAC1B,IAAI,CAAC,wBAAwB,EAAE,CAAC;IAClC,CAAC;IAEO,UAAU,CAAC,IAAY;QAC7B,wDAAwD;QACxD,MAAM,KAAK,GAAyB,IAAI,CAAC,GAAG,CAAC,IAAI,CAC/C,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,CACtD,CAAC;QAEF,OAAO,KAAK,CAAC;IACf,CAAC;IAEO,wBAAwB;QAC9B,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC;QAC1B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE;YAC5B,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE;gBAC1B,OAAO;aACR;YACD,IAAI,CAAC,eAAe,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC;SAC5D;IACH,CAAC;IAED,iEAAiE;IACjE,WAAW,CACT,IAAY,EACZ,OAGI,EAAE;QAEN,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAEpC,sDAAsD;QACtD,IAAI,CAAC,KAAK,EAAE;YACV,MAAM,IAAI,KAAK,CAAC,SAAS,IAAI,4BAA4B,CAAC,CAAC;SAC5D;QAED,0BAA0B;QAC1B,OAAO;YACL,WAAW,EAAE,IAAI,CAAC,OAAO;YACzB,IAAI,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC/B,kBAAkB,EAAE,IAAI,CAAC,kBAAkB,IAAI,KAAK;YACpD,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,KAAK;YAC5C,eAAe,EAAE,IAAI,CAAC,eAAe,IAAI,KAAK;SAC/C,CAAC;IACJ,CAAC;IAED,oDAAoD;IACpD,uBAAuB,CAAC,QAAsB;QAC5C,OAAO,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;CACF"}
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import { Contract } from "./parser";
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
|
-
describe("Contract
|
|
3
|
+
describe("Contract", () => {
|
|
4
4
|
const mockContractAddress = "0x0";
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
const mockContractAbi = [
|
|
6
|
+
{
|
|
7
|
+
type: "event",
|
|
8
|
+
name: "MockEvent",
|
|
9
|
+
kind: "struct",
|
|
10
|
+
members: [{ name: "param1", type: "felt", kind: "key" }],
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
type: "event",
|
|
14
|
+
name: "AnotherEvent",
|
|
15
|
+
kind: "struct",
|
|
16
|
+
members: [{ name: "param1", type: "felt", kind: "key" }],
|
|
17
|
+
},
|
|
18
|
+
];
|
|
12
19
|
it("should create an EventFilter for a valid event name", () => {
|
|
13
20
|
const contract = new Contract(mockContractAddress, mockContractAbi);
|
|
14
21
|
const filter = contract.eventFilter("MockEvent");
|
|
@@ -22,6 +29,17 @@ describe("Contract class tests", () => {
|
|
|
22
29
|
expect(filter).toBeDefined();
|
|
23
30
|
expect(filter.includeReceipt).toBeTruthy();
|
|
24
31
|
});
|
|
32
|
+
it("can lookup an event name by its selector", () => {
|
|
33
|
+
const contract = new Contract(mockContractAddress, mockContractAbi);
|
|
34
|
+
const filter = contract.eventFilter("AnotherEvent");
|
|
35
|
+
const eventName = contract.lookupEventFromSelector(filter.keys?.[0] ?? "0x00");
|
|
36
|
+
expect(eventName).toEqual("AnotherEvent");
|
|
37
|
+
});
|
|
38
|
+
it("returns undefined if no event matches", () => {
|
|
39
|
+
const contract = new Contract(mockContractAddress, mockContractAbi);
|
|
40
|
+
const eventName = contract.lookupEventFromSelector("0x1234567890123456789012345678901234567890123456789012345678901234");
|
|
41
|
+
expect(eventName).toBeUndefined();
|
|
42
|
+
});
|
|
25
43
|
it("should throw an error for an invalid event name", () => {
|
|
26
44
|
const contract = new Contract(mockContractAddress, mockContractAbi);
|
|
27
45
|
expect(() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.test.js","sourceRoot":"","sources":["../../src/starknet/parser.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAI9C,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"parser.test.js","sourceRoot":"","sources":["../../src/starknet/parser.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAI9C,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;IACxB,MAAM,mBAAmB,GAAiB,KAAK,CAAC;IAEhD,MAAM,eAAe,GAAG;QACtB;YACE,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;SACzD;QACD;YACE,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,cAAc;YACpB,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;SACzD;KACF,CAAC;IAEF,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,mBAAmB,EAAE,eAAe,CAAC,CAAC;QACpE,MAAM,MAAM,GAAG,QAAQ,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;QAEjD,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QACrD,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,SAAS,EAAE,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,mBAAmB,EAAE,eAAe,CAAC,CAAC;QACpE,MAAM,MAAM,GAAG,QAAQ,CAAC,WAAW,CAAC,WAAW,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;QAE3E,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,UAAU,EAAE,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,mBAAmB,EAAE,eAAe,CAAC,CAAC;QACpE,MAAM,MAAM,GAAG,QAAQ,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;QACpD,MAAM,SAAS,GAAG,QAAQ,CAAC,uBAAuB,CAChD,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,MAAM,CAC3B,CAAC;QAEF,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,mBAAmB,EAAE,eAAe,CAAC,CAAC;QACpE,MAAM,SAAS,GAAG,QAAQ,CAAC,uBAAuB,CAChD,oEAAoE,CACrE,CAAC;QAEF,MAAM,CAAC,SAAS,CAAC,CAAC,aAAa,EAAE,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,mBAAmB,EAAE,eAAe,CAAC,CAAC;QAEpE,MAAM,CAAC,GAAG,EAAE;YACV,QAAQ,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC,OAAO,CAAC,8CAA8C,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -4,13 +4,19 @@ import { FieldElement } from "./felt";
|
|
|
4
4
|
|
|
5
5
|
describe("FieldElement", () => {
|
|
6
6
|
test("parse address", () => {
|
|
7
|
-
FieldElement.parse(
|
|
7
|
+
const output = FieldElement.parse(
|
|
8
|
+
"0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
|
|
9
|
+
);
|
|
10
|
+
expect(output).toEqual(
|
|
8
11
|
"0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
|
|
9
12
|
);
|
|
10
13
|
});
|
|
11
14
|
|
|
12
15
|
test("parse value", () => {
|
|
13
|
-
FieldElement.parse("0x1");
|
|
16
|
+
const output = FieldElement.parse("0x1");
|
|
17
|
+
expect(output).toEqual(
|
|
18
|
+
"0x0000000000000000000000000000000000000000000000000000000000000001",
|
|
19
|
+
);
|
|
14
20
|
});
|
|
15
21
|
|
|
16
22
|
test("parse invalid value", () => {
|
package/src/starknet/felt.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { hash } from "starknet";
|
|
2
3
|
|
|
3
4
|
export type FieldElement = `0x${string}`;
|
|
4
5
|
|
|
@@ -12,5 +13,18 @@ export const FieldElement = z.string().transform((value, ctx) => {
|
|
|
12
13
|
});
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
const bn = BigInt(value);
|
|
17
|
+
const padded = bn.toString(16).padStart(64, "0");
|
|
18
|
+
return `0x${padded}` as FieldElement;
|
|
16
19
|
});
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Compute the selector from a function or event name.
|
|
23
|
+
*
|
|
24
|
+
* The return value is padded to 32 bytes.
|
|
25
|
+
* @param name the function/event name
|
|
26
|
+
* @returns a field element
|
|
27
|
+
*/
|
|
28
|
+
export function getSelector(name: string): FieldElement {
|
|
29
|
+
return FieldElement.parse(hash.getSelectorFromName(name));
|
|
30
|
+
}
|
package/src/starknet/index.ts
CHANGED
|
@@ -3,16 +3,23 @@ import { describe, expect, it } from "vitest";
|
|
|
3
3
|
import { FieldElement } from "./felt";
|
|
4
4
|
import { EventAbi } from "starknet";
|
|
5
5
|
|
|
6
|
-
describe("Contract
|
|
6
|
+
describe("Contract", () => {
|
|
7
7
|
const mockContractAddress: FieldElement = "0x0";
|
|
8
|
-
const mockEventAbi: EventAbi = {
|
|
9
|
-
type: "event",
|
|
10
|
-
name: "MockEvent",
|
|
11
|
-
kind: "struct",
|
|
12
|
-
members: [{ name: "param1", type: "felt", kind: "key" }],
|
|
13
|
-
};
|
|
14
8
|
|
|
15
|
-
const mockContractAbi = [
|
|
9
|
+
const mockContractAbi = [
|
|
10
|
+
{
|
|
11
|
+
type: "event",
|
|
12
|
+
name: "MockEvent",
|
|
13
|
+
kind: "struct",
|
|
14
|
+
members: [{ name: "param1", type: "felt", kind: "key" }],
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
type: "event",
|
|
18
|
+
name: "AnotherEvent",
|
|
19
|
+
kind: "struct",
|
|
20
|
+
members: [{ name: "param1", type: "felt", kind: "key" }],
|
|
21
|
+
},
|
|
22
|
+
];
|
|
16
23
|
|
|
17
24
|
it("should create an EventFilter for a valid event name", () => {
|
|
18
25
|
const contract = new Contract(mockContractAddress, mockContractAbi);
|
|
@@ -31,6 +38,25 @@ describe("Contract class tests", () => {
|
|
|
31
38
|
expect(filter.includeReceipt).toBeTruthy();
|
|
32
39
|
});
|
|
33
40
|
|
|
41
|
+
it("can lookup an event name by its selector", () => {
|
|
42
|
+
const contract = new Contract(mockContractAddress, mockContractAbi);
|
|
43
|
+
const filter = contract.eventFilter("AnotherEvent");
|
|
44
|
+
const eventName = contract.lookupEventFromSelector(
|
|
45
|
+
filter.keys?.[0] ?? "0x00",
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
expect(eventName).toEqual("AnotherEvent");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("returns undefined if no event matches", () => {
|
|
52
|
+
const contract = new Contract(mockContractAddress, mockContractAbi);
|
|
53
|
+
const eventName = contract.lookupEventFromSelector(
|
|
54
|
+
"0x1234567890123456789012345678901234567890123456789012345678901234",
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
expect(eventName).toBeUndefined();
|
|
58
|
+
});
|
|
59
|
+
|
|
34
60
|
it("should throw an error for an invalid event name", () => {
|
|
35
61
|
const contract = new Contract(mockContractAddress, mockContractAbi);
|
|
36
62
|
|
package/src/starknet/parser.ts
CHANGED
|
@@ -1,28 +1,42 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { EventFilter } from "./filter";
|
|
1
|
+
import { type Abi, EventAbi } from "starknet";
|
|
3
2
|
|
|
4
|
-
import {
|
|
3
|
+
import { FieldElement, getSelector } from "./felt";
|
|
4
|
+
import { EventFilter } from "./filter";
|
|
5
5
|
|
|
6
6
|
/** Build a stream filter from a contract ABI. */
|
|
7
7
|
export class Contract {
|
|
8
8
|
// Read-only properties for the contract's address and its ABI.
|
|
9
|
-
readonly
|
|
10
|
-
readonly
|
|
9
|
+
readonly address: FieldElement;
|
|
10
|
+
readonly abi: Abi;
|
|
11
|
+
|
|
12
|
+
private selectorToEvent: Record<FieldElement, string>;
|
|
11
13
|
|
|
12
14
|
constructor(contractAddress: FieldElement, contractAbi: Abi) {
|
|
13
|
-
this.
|
|
14
|
-
this.
|
|
15
|
+
this.address = contractAddress;
|
|
16
|
+
this.abi = contractAbi;
|
|
17
|
+
this.selectorToEvent = {};
|
|
18
|
+
this._populateSelectorToEvent();
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
private _findEvent(name: string): EventAbi | undefined {
|
|
18
22
|
// Find the event in the ABI matching the provided name.
|
|
19
|
-
const event: EventAbi | undefined = this.
|
|
23
|
+
const event: EventAbi | undefined = this.abi.find(
|
|
20
24
|
(item) => item.type === "event" && item.name === name,
|
|
21
25
|
);
|
|
22
26
|
|
|
23
27
|
return event;
|
|
24
28
|
}
|
|
25
29
|
|
|
30
|
+
private _populateSelectorToEvent(): void {
|
|
31
|
+
this.selectorToEvent = {};
|
|
32
|
+
for (const event of this.abi) {
|
|
33
|
+
if (event.type !== "event") {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
this.selectorToEvent[getSelector(event.name)] = event.name;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
26
40
|
/** Filter events based on their name from the contract's ABI. */
|
|
27
41
|
eventFilter(
|
|
28
42
|
name: string,
|
|
@@ -40,11 +54,16 @@ export class Contract {
|
|
|
40
54
|
|
|
41
55
|
// Return the found event.
|
|
42
56
|
return {
|
|
43
|
-
fromAddress: this.
|
|
44
|
-
keys: [
|
|
57
|
+
fromAddress: this.address,
|
|
58
|
+
keys: [getSelector(event.name)],
|
|
45
59
|
includeTransaction: opts.includeTransaction ?? false,
|
|
46
60
|
includeReceipt: opts.includeReceipt ?? false,
|
|
47
61
|
includeReverted: opts.includeReverted ?? false,
|
|
48
62
|
};
|
|
49
63
|
}
|
|
64
|
+
|
|
65
|
+
/** Returns the event name based on its selector. */
|
|
66
|
+
lookupEventFromSelector(selector: FieldElement): string | undefined {
|
|
67
|
+
return this.selectorToEvent[selector];
|
|
68
|
+
}
|
|
50
69
|
}
|