From cc5b5b2a5cf090ab9dedb6a29e685dc277a323cb Mon Sep 17 00:00:00 2001 From: 0xkenj1 Date: Tue, 15 Oct 2024 16:26:28 -0300 Subject: [PATCH 1/8] feat: registry base --- .../processors/src/allo/allo.processor.ts | 2 +- .../exceptions/unsupportedEvent.exception.ts | 10 ++++++ .../src/interfaces/factory.interface.ts | 19 ++++++++++++ packages/processors/src/interfaces/index.ts | 1 + .../src/interfaces/processor.interface.ts | 2 +- .../registry/handlers/roleGranted.handler.ts | 20 ++++++++++++ packages/processors/src/registry/index.ts | 1 + .../src/registry/registry.processor.ts | 25 +++++++++++++++ .../src/registry/registryProcessorFactory.ts | 31 +++++++++++++++++++ .../src/strategy/strategy.processor.ts | 2 +- packages/shared/src/types/events/common.ts | 21 ++++++++++--- packages/shared/src/types/events/registry.ts | 26 ++++++++++++++++ 12 files changed, 153 insertions(+), 7 deletions(-) create mode 100644 packages/processors/src/exceptions/unsupportedEvent.exception.ts create mode 100644 packages/processors/src/interfaces/factory.interface.ts create mode 100644 packages/processors/src/registry/handlers/roleGranted.handler.ts create mode 100644 packages/processors/src/registry/index.ts create mode 100644 packages/processors/src/registry/registry.processor.ts create mode 100644 packages/processors/src/registry/registryProcessorFactory.ts diff --git a/packages/processors/src/allo/allo.processor.ts b/packages/processors/src/allo/allo.processor.ts index b59e1ef..3997451 100644 --- a/packages/processors/src/allo/allo.processor.ts +++ b/packages/processors/src/allo/allo.processor.ts @@ -5,7 +5,7 @@ import type { IProcessor } from "../internal.js"; export class AlloProcessor implements IProcessor<"Allo", AlloEvent> { //TODO: Implement - process(_event: ProtocolEvent<"Allo", AlloEvent>): Promise { + process(_event: ProtocolEvent<"Allo", AlloEvent>): Promise { throw new Error("Method not implemented."); } } diff --git a/packages/processors/src/exceptions/unsupportedEvent.exception.ts b/packages/processors/src/exceptions/unsupportedEvent.exception.ts new file mode 100644 index 0000000..4760f12 --- /dev/null +++ b/packages/processors/src/exceptions/unsupportedEvent.exception.ts @@ -0,0 +1,10 @@ +import { ContractName } from "@grants-stack-indexer/shared"; + +export class UnsupportedEventException extends Error { + constructor( + contract: ContractName, + public readonly eventName: string, + ) { + super(`Event ${eventName} unsupported for ${contract} processor`); + } +} diff --git a/packages/processors/src/interfaces/factory.interface.ts b/packages/processors/src/interfaces/factory.interface.ts new file mode 100644 index 0000000..506b725 --- /dev/null +++ b/packages/processors/src/interfaces/factory.interface.ts @@ -0,0 +1,19 @@ +import { PublicClient } from "viem"; + +import { IMetadataProvider } from "@grants-stack-indexer/metadata"; +import { IPricingProvider } from "@grants-stack-indexer/pricing"; +import { Changeset } from "@grants-stack-indexer/repository"; +import { ContractName, ContractToEventName, ProtocolEvent } from "@grants-stack-indexer/shared"; + +export interface IEventHandler { + handle(): Promise; +} + +export interface IEventHandlerFactory> { + createHandler( + event: ProtocolEvent, + pricingProvider: IPricingProvider, + metadataProvider: IMetadataProvider, + viemProvider: PublicClient, + ): IEventHandler; +} diff --git a/packages/processors/src/interfaces/index.ts b/packages/processors/src/interfaces/index.ts index d9d3380..c5609d3 100644 --- a/packages/processors/src/interfaces/index.ts +++ b/packages/processors/src/interfaces/index.ts @@ -1 +1,2 @@ export * from "./processor.interface.js"; +export * from "./factory.interface.js"; diff --git a/packages/processors/src/interfaces/processor.interface.ts b/packages/processors/src/interfaces/processor.interface.ts index f8aeb02..4f8c943 100644 --- a/packages/processors/src/interfaces/processor.interface.ts +++ b/packages/processors/src/interfaces/processor.interface.ts @@ -7,5 +7,5 @@ export interface IProcessor): Promise; + process(event: ProtocolEvent): Promise; } diff --git a/packages/processors/src/registry/handlers/roleGranted.handler.ts b/packages/processors/src/registry/handlers/roleGranted.handler.ts new file mode 100644 index 0000000..e5be7b3 --- /dev/null +++ b/packages/processors/src/registry/handlers/roleGranted.handler.ts @@ -0,0 +1,20 @@ +import { PublicClient } from "viem"; + +import { IMetadataProvider } from "@grants-stack-indexer/metadata"; +import { IPricingProvider } from "@grants-stack-indexer/pricing"; +import { Changeset } from "@grants-stack-indexer/repository"; +import { ProtocolEvent } from "@grants-stack-indexer/shared"; + +import { IEventHandler } from "../../internal.js"; + +export class RoleGrantedHandler implements IEventHandler { + constructor( + private readonly event: ProtocolEvent<"Registry", "RoleGranted">, + private readonly pricingProvider: IPricingProvider, + private readonly metadataProvider: IMetadataProvider, + private readonly viemProvider: PublicClient, + ) {} + async handle(): Promise { + return []; + } +} diff --git a/packages/processors/src/registry/index.ts b/packages/processors/src/registry/index.ts new file mode 100644 index 0000000..ea4498b --- /dev/null +++ b/packages/processors/src/registry/index.ts @@ -0,0 +1 @@ +export * from "./registry.processor.js"; diff --git a/packages/processors/src/registry/registry.processor.ts b/packages/processors/src/registry/registry.processor.ts new file mode 100644 index 0000000..34ef138 --- /dev/null +++ b/packages/processors/src/registry/registry.processor.ts @@ -0,0 +1,25 @@ +import { PublicClient } from "viem"; + +import { IMetadataProvider } from "@grants-stack-indexer/metadata"; +import { IPricingProvider } from "@grants-stack-indexer/pricing"; +import { Changeset } from "@grants-stack-indexer/repository"; +import { ProtocolEvent, RegistryEvent } from "@grants-stack-indexer/shared"; + +import type { IProcessor } from "../internal.js"; +import { RegistryHandlerFactory } from "./registryProcessorFactory.js"; + +export class RegistryProcessor implements IProcessor<"Registry", RegistryEvent> { + private factory: RegistryHandlerFactory = new RegistryHandlerFactory(); + constructor( + private readonly pricingProvider: IPricingProvider, + private readonly metadataProvider: IMetadataProvider, + private readonly viemProvider: PublicClient, + ) {} + //TODO: Implement + async process(_event: ProtocolEvent<"Registry", RegistryEvent>): Promise { + return await this.factory + .createHandler(_event, this.pricingProvider, this.metadataProvider, this.viemProvider) + .handle(); + throw new Error("Method not implemented."); + } +} diff --git a/packages/processors/src/registry/registryProcessorFactory.ts b/packages/processors/src/registry/registryProcessorFactory.ts new file mode 100644 index 0000000..8446be6 --- /dev/null +++ b/packages/processors/src/registry/registryProcessorFactory.ts @@ -0,0 +1,31 @@ +// registry processor factory for each of the events + +import { PublicClient } from "viem"; + +import { IMetadataProvider } from "@grants-stack-indexer/metadata"; +import { IPricingProvider } from "@grants-stack-indexer/pricing"; +import { ProtocolEvent, RegistryEvent } from "@grants-stack-indexer/shared"; + +import { UnsupportedEventException } from "../exceptions/unsupportedEvent.exception.js"; +import { IEventHandler, IEventHandlerFactory } from "../internal.js"; +import { RoleGrantedHandler } from "./handlers/roleGranted.handler.js"; + +export class RegistryHandlerFactory implements IEventHandlerFactory<"Registry", RegistryEvent> { + public createHandler( + event: ProtocolEvent<"Registry", RegistryEvent>, + pricingProvider: IPricingProvider, + metadataProvider: IMetadataProvider, + viemProvider: PublicClient, + ): IEventHandler { + if (isRoleGranted(event)) { + return new RoleGrantedHandler(event, pricingProvider, metadataProvider, viemProvider); + } + throw new UnsupportedEventException("Registry", event.eventName as string); + } +} + +const isRoleGranted = ( + event: ProtocolEvent<"Registry", RegistryEvent>, +): event is ProtocolEvent<"Registry", "RoleGranted"> => { + return event.eventName === "RoleGranted"; +}; diff --git a/packages/processors/src/strategy/strategy.processor.ts b/packages/processors/src/strategy/strategy.processor.ts index 6f4d499..9c2c4ae 100644 --- a/packages/processors/src/strategy/strategy.processor.ts +++ b/packages/processors/src/strategy/strategy.processor.ts @@ -4,7 +4,7 @@ import { ProtocolEvent, StrategyEvent } from "@grants-stack-indexer/shared"; import type { IProcessor } from "../internal.js"; export class StrategyProcessor implements IProcessor<"Strategy", StrategyEvent> { - process(_event: ProtocolEvent<"Strategy", StrategyEvent>): Promise { + process(_event: ProtocolEvent<"Strategy", StrategyEvent>): Promise { //TODO: Implement throw new Error("Method not implemented."); } diff --git a/packages/shared/src/types/events/common.ts b/packages/shared/src/types/events/common.ts index 3b4921b..c210cc4 100644 --- a/packages/shared/src/types/events/common.ts +++ b/packages/shared/src/types/events/common.ts @@ -1,7 +1,14 @@ import { Address } from "../../internal.js"; -import { AlloEvent, AlloEventParams, StrategyEvent, StrategyEventParams } from "./index.js"; +import { + AlloEvent, + AlloEventParams, + RegistryEvent, + RegistryEventParams, + StrategyEvent, + StrategyEventParams, +} from "./index.js"; -export type ContractName = "Strategy" | "Allo"; +export type ContractName = "Strategy" | "Allo" | "Registry"; export type AnyEvent = StrategyEvent | AlloEvent; /** @@ -11,7 +18,9 @@ export type ContractToEventName = T extends "Allo" ? AlloEvent : T extends "Strategy" ? StrategyEvent - : never; + : T extends "Registry" + ? RegistryEvent + : never; /** * This type is used to map contract names to their respective event parameters. @@ -24,7 +33,11 @@ export type EventParams ? E extends StrategyEvent ? StrategyEventParams : never - : never; + : T extends "Registry" + ? E extends RegistryEvent + ? RegistryEventParams + : never + : never; /** * This type is used to represent a protocol event. diff --git a/packages/shared/src/types/events/registry.ts b/packages/shared/src/types/events/registry.ts index 46e4ac1..b4b4c54 100644 --- a/packages/shared/src/types/events/registry.ts +++ b/packages/shared/src/types/events/registry.ts @@ -4,3 +4,29 @@ // | "ProfileMetadataUpdated" // | "ProfileNameUpdated" // | "ProfileOwnerUpdated"; + +import { Address } from "../../internal.js"; + +/** + * This type is used to represent a Registry events. + */ +export type RegistryEvent = "ProfileCreated" | "RoleGranted"; + +/** + * This type maps Registry events to their respective parameters. + */ +export type RegistryEventParams = T extends "ProfileCreated" + ? "ProfileCreatedParams" + : T extends "RoleGranted" + ? RoleGrantedParams + : never; + +// ============================================================================= +// =============================== Event Parameters ============================ +// ============================================================================= +export type ProfileCreatedParams = { + contractAddress: Address; +}; +export type RoleGrantedParams = { + contractAddress: Address; +}; From 105a70b7724257c842a5d871e114607325e9cd11 Mon Sep 17 00:00:00 2001 From: 0xkenj1 Date: Wed, 16 Oct 2024 12:19:11 -0300 Subject: [PATCH 2/8] feat: roleGranted --- apps/indexer/config.yaml | 7 ++- packages/processors/package.json | 1 + .../src/interfaces/factory.interface.ts | 5 +- .../handlers/profileCreated.hanlder.ts | 21 ++++++++ .../registry/handlers/roleGranted.handler.ts | 53 +++++++++++++++++-- .../src/registry/registry.processor.ts | 14 +++-- .../src/registry/registryProcessorFactory.ts | 12 ++++- packages/shared/src/constants/address.ts | 5 ++ packages/shared/src/external.ts | 2 +- packages/shared/src/types/common.ts | 2 + packages/shared/src/types/events/registry.ts | 15 ++++-- pnpm-lock.yaml | 3 ++ 12 files changed, 124 insertions(+), 16 deletions(-) create mode 100644 packages/processors/src/registry/handlers/profileCreated.hanlder.ts diff --git a/apps/indexer/config.yaml b/apps/indexer/config.yaml index 0ac8873..de28588 100644 --- a/apps/indexer/config.yaml +++ b/apps/indexer/config.yaml @@ -9,10 +9,10 @@ contracts: handler: src/handlers/Allo.ts events: - event: PoolCreated(uint256 indexed poolId, bytes32 indexed profileId, address strategy, address token, uint256 amount, (uint256,string) metadata) - - event: RoleGranted(uint64 indexed roleId, address indexed account, uint32 delay, uint48 since, bool newMember) - event: PoolMetadataUpdated(uint256 indexed poolId, (uint256,string) metadata) - event: PoolFunded(uint256 indexed poolId, uint256 amount, uint256 fee) - - event: RoleRevoked(uint64 indexed roleId, address indexed account) + - event: RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); + - event: RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - name: Registry handler: src/handlers/Registry.ts events: @@ -20,6 +20,9 @@ contracts: - event: ProfileMetadataUpdated(bytes32 indexed profileId, (uint256,string) metadata) - event: ProfileNameUpdated(bytes32 indexed profileId, string name, address anchor) - event: ProfileOwnerUpdated(bytes32 indexed profileId, address owner) + - event: RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); + - event: RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); + - name: Strategy handler: src/handlers/Strategy.ts events: diff --git a/packages/processors/package.json b/packages/processors/package.json index 5fec498..a562428 100644 --- a/packages/processors/package.json +++ b/packages/processors/package.json @@ -28,6 +28,7 @@ "test:cov": "vitest run --config vitest.config.ts --coverage" }, "dependencies": { + "@grants-stack-indexer/chain-providers": "workspace:*", "@grants-stack-indexer/metadata": "workspace:*", "@grants-stack-indexer/pricing": "workspace:*", "@grants-stack-indexer/repository": "workspace:*", diff --git a/packages/processors/src/interfaces/factory.interface.ts b/packages/processors/src/interfaces/factory.interface.ts index 506b725..63783d6 100644 --- a/packages/processors/src/interfaces/factory.interface.ts +++ b/packages/processors/src/interfaces/factory.interface.ts @@ -2,7 +2,7 @@ import { PublicClient } from "viem"; import { IMetadataProvider } from "@grants-stack-indexer/metadata"; import { IPricingProvider } from "@grants-stack-indexer/pricing"; -import { Changeset } from "@grants-stack-indexer/repository"; +import { Changeset, IProjectReadRepository } from "@grants-stack-indexer/repository"; import { ContractName, ContractToEventName, ProtocolEvent } from "@grants-stack-indexer/shared"; export interface IEventHandler { @@ -14,6 +14,9 @@ export interface IEventHandlerFactory, pricingProvider: IPricingProvider, metadataProvider: IMetadataProvider, + repositories: { + project: IProjectReadRepository; + }, viemProvider: PublicClient, ): IEventHandler; } diff --git a/packages/processors/src/registry/handlers/profileCreated.hanlder.ts b/packages/processors/src/registry/handlers/profileCreated.hanlder.ts new file mode 100644 index 0000000..bdf102c --- /dev/null +++ b/packages/processors/src/registry/handlers/profileCreated.hanlder.ts @@ -0,0 +1,21 @@ +import { PublicClient } from "viem"; + +import { IMetadataProvider } from "@grants-stack-indexer/metadata"; +import { IPricingProvider } from "@grants-stack-indexer/pricing"; +import { Changeset, IProjectReadRepository } from "@grants-stack-indexer/repository"; +import { ProtocolEvent } from "@grants-stack-indexer/shared"; + +import { IEventHandler } from "../../internal.js"; + +export class ProfileCreatedHandler implements IEventHandler { + constructor( + private readonly event: ProtocolEvent<"Registry", "ProfileCreated">, + private readonly pricingProvider: IPricingProvider, + private readonly metadataProvider: IMetadataProvider, + private readonly projectRepository: IProjectReadRepository, + private readonly viemProvider: PublicClient, + ) {} + async handle(): Promise { + return []; + } +} diff --git a/packages/processors/src/registry/handlers/roleGranted.handler.ts b/packages/processors/src/registry/handlers/roleGranted.handler.ts index e5be7b3..60d84ea 100644 --- a/packages/processors/src/registry/handlers/roleGranted.handler.ts +++ b/packages/processors/src/registry/handlers/roleGranted.handler.ts @@ -1,9 +1,9 @@ -import { PublicClient } from "viem"; +import { getAddress, PublicClient } from "viem"; import { IMetadataProvider } from "@grants-stack-indexer/metadata"; import { IPricingProvider } from "@grants-stack-indexer/pricing"; -import { Changeset } from "@grants-stack-indexer/repository"; -import { ProtocolEvent } from "@grants-stack-indexer/shared"; +import { Changeset, IProjectReadRepository } from "@grants-stack-indexer/repository"; +import { ALLO_OWNER_ROLE, ChainId, ProtocolEvent } from "@grants-stack-indexer/shared"; import { IEventHandler } from "../../internal.js"; @@ -12,9 +12,54 @@ export class RoleGrantedHandler implements IEventHandler { private readonly event: ProtocolEvent<"Registry", "RoleGranted">, private readonly pricingProvider: IPricingProvider, private readonly metadataProvider: IMetadataProvider, + private readonly projectRepository: IProjectReadRepository, private readonly viemProvider: PublicClient, ) {} async handle(): Promise { - return []; + const role = this.event.params.role.toLocaleLowerCase(); + if (role === ALLO_OWNER_ROLE) { + return []; + } + + const account = getAddress(this.event.params.account); + const project = await this.projectRepository.getProjectById( + this.event.chainId as ChainId, + role, + ); + // The member role for an Allo V2 profile, is the profileId itself. + // If a project exists with that id, we create the member role + // If it doesn't exists we create a pending project role. This can happens + // when a new project is created, since in Allo V2 the RoleGranted event for a member is + // emitted before the ProfileCreated event. + if (project) { + return [ + { + type: "InsertProjectRole", + args: { + projectRole: { + chainId: this.event.chainId as ChainId, + projectId: project.id, + address: account, + role: "member", + createdAtBlock: BigInt(this.event.blockNumber), + }, + }, + }, + ]; + } + + return [ + { + type: "InsertPendingProjectRole", + args: { + pendingProjectRole: { + chainId: this.event.chainId as ChainId, + role: role, + address: account, + createdAtBlock: BigInt(this.event.blockNumber), + }, + }, + }, + ]; } } diff --git a/packages/processors/src/registry/registry.processor.ts b/packages/processors/src/registry/registry.processor.ts index 34ef138..15f8a07 100644 --- a/packages/processors/src/registry/registry.processor.ts +++ b/packages/processors/src/registry/registry.processor.ts @@ -2,7 +2,7 @@ import { PublicClient } from "viem"; import { IMetadataProvider } from "@grants-stack-indexer/metadata"; import { IPricingProvider } from "@grants-stack-indexer/pricing"; -import { Changeset } from "@grants-stack-indexer/repository"; +import { Changeset, IProjectReadRepository } from "@grants-stack-indexer/repository"; import { ProtocolEvent, RegistryEvent } from "@grants-stack-indexer/shared"; import type { IProcessor } from "../internal.js"; @@ -13,13 +13,21 @@ export class RegistryProcessor implements IProcessor<"Registry", RegistryEvent> constructor( private readonly pricingProvider: IPricingProvider, private readonly metadataProvider: IMetadataProvider, + private readonly dependencies: { + project: IProjectReadRepository; + }, private readonly viemProvider: PublicClient, ) {} //TODO: Implement async process(_event: ProtocolEvent<"Registry", RegistryEvent>): Promise { return await this.factory - .createHandler(_event, this.pricingProvider, this.metadataProvider, this.viemProvider) + .createHandler( + _event, + this.pricingProvider, + this.metadataProvider, + this.dependencies, + this.viemProvider, + ) .handle(); - throw new Error("Method not implemented."); } } diff --git a/packages/processors/src/registry/registryProcessorFactory.ts b/packages/processors/src/registry/registryProcessorFactory.ts index 8446be6..79e42ea 100644 --- a/packages/processors/src/registry/registryProcessorFactory.ts +++ b/packages/processors/src/registry/registryProcessorFactory.ts @@ -4,6 +4,7 @@ import { PublicClient } from "viem"; import { IMetadataProvider } from "@grants-stack-indexer/metadata"; import { IPricingProvider } from "@grants-stack-indexer/pricing"; +import { IProjectReadRepository } from "@grants-stack-indexer/repository"; import { ProtocolEvent, RegistryEvent } from "@grants-stack-indexer/shared"; import { UnsupportedEventException } from "../exceptions/unsupportedEvent.exception.js"; @@ -15,10 +16,19 @@ export class RegistryHandlerFactory implements IEventHandlerFactory<"Registry", event: ProtocolEvent<"Registry", RegistryEvent>, pricingProvider: IPricingProvider, metadataProvider: IMetadataProvider, + repositories: { + project: IProjectReadRepository; + }, viemProvider: PublicClient, ): IEventHandler { if (isRoleGranted(event)) { - return new RoleGrantedHandler(event, pricingProvider, metadataProvider, viemProvider); + return new RoleGrantedHandler( + event, + pricingProvider, + metadataProvider, + repositories.project, + viemProvider, + ); } throw new UnsupportedEventException("Registry", event.eventName as string); } diff --git a/packages/shared/src/constants/address.ts b/packages/shared/src/constants/address.ts index e1884d2..01e7edc 100644 --- a/packages/shared/src/constants/address.ts +++ b/packages/shared/src/constants/address.ts @@ -1,5 +1,10 @@ import { Address } from "viem"; +import { Bytes32String } from "../internal.js"; + +export const ALLO_OWNER_ROLE = + "0x815b5a78dc333d344c7df9da23c04dbd432015cc701876ddb9ffe850e6882747" as Bytes32String; + export const NATIVE_TOKEN_ADDRESS: Address = "0x0000000000000000000000000000000000000001"; export const isNativeToken = (address: Address): boolean => { diff --git a/packages/shared/src/external.ts b/packages/shared/src/external.ts index 29e4880..f947438 100644 --- a/packages/shared/src/external.ts +++ b/packages/shared/src/external.ts @@ -1,4 +1,4 @@ export type * from "./types/index.js"; export type { Address } from "./internal.js"; export type { ILogger, Logger } from "./internal.js"; -export { NATIVE_TOKEN_ADDRESS, isNativeToken } from "./constants/index.js"; +export { NATIVE_TOKEN_ADDRESS, isNativeToken, ALLO_OWNER_ROLE } from "./constants/index.js"; diff --git a/packages/shared/src/types/common.ts b/packages/shared/src/types/common.ts index d9128ec..ca5e528 100644 --- a/packages/shared/src/types/common.ts +++ b/packages/shared/src/types/common.ts @@ -1,3 +1,5 @@ import { Branded } from "../internal.js"; export type ChainId = Branded; + +export type Bytes32String = Branded<`0x${string}`, "Bytes32String">; diff --git a/packages/shared/src/types/events/registry.ts b/packages/shared/src/types/events/registry.ts index b4b4c54..d47c2f6 100644 --- a/packages/shared/src/types/events/registry.ts +++ b/packages/shared/src/types/events/registry.ts @@ -5,7 +5,7 @@ // | "ProfileNameUpdated" // | "ProfileOwnerUpdated"; -import { Address } from "../../internal.js"; +import { Address, Bytes32String } from "../../internal.js"; /** * This type is used to represent a Registry events. @@ -16,7 +16,7 @@ export type RegistryEvent = "ProfileCreated" | "RoleGranted"; * This type maps Registry events to their respective parameters. */ export type RegistryEventParams = T extends "ProfileCreated" - ? "ProfileCreatedParams" + ? ProfileCreatedParams : T extends "RoleGranted" ? RoleGrantedParams : never; @@ -25,8 +25,15 @@ export type RegistryEventParams = T extends "ProfileCre // =============================== Event Parameters ============================ // ============================================================================= export type ProfileCreatedParams = { - contractAddress: Address; + profileId: Bytes32String; + nonce: bigint; + name: string; + metadata: [protocol: bigint, pointer: string]; + owner: Address; + anchor: Address; }; export type RoleGrantedParams = { - contractAddress: Address; + role: Bytes32String; + account: Address; + sender: Address; }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7cd70c6..e7af5f0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -160,6 +160,9 @@ importers: packages/processors: dependencies: + "@grants-stack-indexer/chain-providers": + specifier: workspace:* + version: link:../chain-providers "@grants-stack-indexer/metadata": specifier: workspace:* version: link:../metadata From fcf00b0f84856739264fb5d4e7d9c380ae323ec8 Mon Sep 17 00:00:00 2001 From: 0xkenj1 Date: Fri, 18 Oct 2024 12:22:24 -0300 Subject: [PATCH 3/8] feat: role granted --- apps/indexer/src/handlers/Registry.ts | 6 +++++ .../src/interfaces/factory.interface.ts | 21 +++++++-------- .../registry/handlers/roleGranted.handler.ts | 21 +++++++-------- .../src/registry/registry.processor.ts | 27 +++++-------------- .../src/registry/registryProcessorFactory.ts | 26 ++++-------------- packages/processors/src/types/index.ts | 1 + .../processors/src/types/processor.types.ts | 15 +++++++++++ 7 files changed, 52 insertions(+), 65 deletions(-) create mode 100644 packages/processors/src/types/index.ts create mode 100644 packages/processors/src/types/processor.types.ts diff --git a/apps/indexer/src/handlers/Registry.ts b/apps/indexer/src/handlers/Registry.ts index 722ff0e..f597722 100644 --- a/apps/indexer/src/handlers/Registry.ts +++ b/apps/indexer/src/handlers/Registry.ts @@ -1,6 +1,12 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { Registry } from "../../generated"; +// Handler for ProfileNameUpdated event +Registry.RoleGranted.handler(async ({}) => {}); + +// Handler for ProfileOwnerUpdated event +Registry.RoleRevoked.handler(async ({}) => {}); + // Handler for ProfileCreated event Registry.ProfileCreated.handler(async ({}) => {}); diff --git a/packages/processors/src/interfaces/factory.interface.ts b/packages/processors/src/interfaces/factory.interface.ts index 63783d6..072d4a3 100644 --- a/packages/processors/src/interfaces/factory.interface.ts +++ b/packages/processors/src/interfaces/factory.interface.ts @@ -1,9 +1,12 @@ -import { PublicClient } from "viem"; +import { Changeset } from "@grants-stack-indexer/repository"; +import { + ChainId, + ContractName, + ContractToEventName, + ProtocolEvent, +} from "@grants-stack-indexer/shared"; -import { IMetadataProvider } from "@grants-stack-indexer/metadata"; -import { IPricingProvider } from "@grants-stack-indexer/pricing"; -import { Changeset, IProjectReadRepository } from "@grants-stack-indexer/repository"; -import { ContractName, ContractToEventName, ProtocolEvent } from "@grants-stack-indexer/shared"; +import { ProcessorDependencies } from "../types/processor.types.js"; export interface IEventHandler { handle(): Promise; @@ -12,11 +15,7 @@ export interface IEventHandler { export interface IEventHandlerFactory> { createHandler( event: ProtocolEvent, - pricingProvider: IPricingProvider, - metadataProvider: IMetadataProvider, - repositories: { - project: IProjectReadRepository; - }, - viemProvider: PublicClient, + chainId: ChainId, + dependencies: ProcessorDependencies, ): IEventHandler; } diff --git a/packages/processors/src/registry/handlers/roleGranted.handler.ts b/packages/processors/src/registry/handlers/roleGranted.handler.ts index 60d84ea..650f519 100644 --- a/packages/processors/src/registry/handlers/roleGranted.handler.ts +++ b/packages/processors/src/registry/handlers/roleGranted.handler.ts @@ -1,19 +1,16 @@ -import { getAddress, PublicClient } from "viem"; +import { getAddress } from "viem"; -import { IMetadataProvider } from "@grants-stack-indexer/metadata"; -import { IPricingProvider } from "@grants-stack-indexer/pricing"; -import { Changeset, IProjectReadRepository } from "@grants-stack-indexer/repository"; +import { Changeset } from "@grants-stack-indexer/repository"; import { ALLO_OWNER_ROLE, ChainId, ProtocolEvent } from "@grants-stack-indexer/shared"; import { IEventHandler } from "../../internal.js"; +import { ProcessorDependencies } from "../../types/processor.types.js"; export class RoleGrantedHandler implements IEventHandler { constructor( private readonly event: ProtocolEvent<"Registry", "RoleGranted">, - private readonly pricingProvider: IPricingProvider, - private readonly metadataProvider: IMetadataProvider, - private readonly projectRepository: IProjectReadRepository, - private readonly viemProvider: PublicClient, + private readonly chainId: ChainId, + private readonly dependencies: ProcessorDependencies, ) {} async handle(): Promise { const role = this.event.params.role.toLocaleLowerCase(); @@ -22,8 +19,8 @@ export class RoleGrantedHandler implements IEventHandler { } const account = getAddress(this.event.params.account); - const project = await this.projectRepository.getProjectById( - this.event.chainId as ChainId, + const project = await this.dependencies.projectRepository.getProjectById( + this.chainId, role, ); // The member role for an Allo V2 profile, is the profileId itself. @@ -37,7 +34,7 @@ export class RoleGrantedHandler implements IEventHandler { type: "InsertProjectRole", args: { projectRole: { - chainId: this.event.chainId as ChainId, + chainId: this.chainId, projectId: project.id, address: account, role: "member", @@ -53,7 +50,7 @@ export class RoleGrantedHandler implements IEventHandler { type: "InsertPendingProjectRole", args: { pendingProjectRole: { - chainId: this.event.chainId as ChainId, + chainId: this.chainId, role: role, address: account, createdAtBlock: BigInt(this.event.blockNumber), diff --git a/packages/processors/src/registry/registry.processor.ts b/packages/processors/src/registry/registry.processor.ts index 15f8a07..ce3023e 100644 --- a/packages/processors/src/registry/registry.processor.ts +++ b/packages/processors/src/registry/registry.processor.ts @@ -1,33 +1,18 @@ -import { PublicClient } from "viem"; - -import { IMetadataProvider } from "@grants-stack-indexer/metadata"; -import { IPricingProvider } from "@grants-stack-indexer/pricing"; -import { Changeset, IProjectReadRepository } from "@grants-stack-indexer/repository"; -import { ProtocolEvent, RegistryEvent } from "@grants-stack-indexer/shared"; +import { Changeset } from "@grants-stack-indexer/repository"; +import { ChainId, ProtocolEvent, RegistryEvent } from "@grants-stack-indexer/shared"; import type { IProcessor } from "../internal.js"; +import { ProcessorDependencies } from "../types/processor.types.js"; import { RegistryHandlerFactory } from "./registryProcessorFactory.js"; export class RegistryProcessor implements IProcessor<"Registry", RegistryEvent> { private factory: RegistryHandlerFactory = new RegistryHandlerFactory(); constructor( - private readonly pricingProvider: IPricingProvider, - private readonly metadataProvider: IMetadataProvider, - private readonly dependencies: { - project: IProjectReadRepository; - }, - private readonly viemProvider: PublicClient, + private readonly chainId: ChainId, + private readonly dependencies: ProcessorDependencies, ) {} //TODO: Implement async process(_event: ProtocolEvent<"Registry", RegistryEvent>): Promise { - return await this.factory - .createHandler( - _event, - this.pricingProvider, - this.metadataProvider, - this.dependencies, - this.viemProvider, - ) - .handle(); + return await this.factory.createHandler(_event, this.chainId, this.dependencies).handle(); } } diff --git a/packages/processors/src/registry/registryProcessorFactory.ts b/packages/processors/src/registry/registryProcessorFactory.ts index 79e42ea..43bed0e 100644 --- a/packages/processors/src/registry/registryProcessorFactory.ts +++ b/packages/processors/src/registry/registryProcessorFactory.ts @@ -1,34 +1,18 @@ -// registry processor factory for each of the events - -import { PublicClient } from "viem"; - -import { IMetadataProvider } from "@grants-stack-indexer/metadata"; -import { IPricingProvider } from "@grants-stack-indexer/pricing"; -import { IProjectReadRepository } from "@grants-stack-indexer/repository"; -import { ProtocolEvent, RegistryEvent } from "@grants-stack-indexer/shared"; +import { ChainId, ProtocolEvent, RegistryEvent } from "@grants-stack-indexer/shared"; import { UnsupportedEventException } from "../exceptions/unsupportedEvent.exception.js"; import { IEventHandler, IEventHandlerFactory } from "../internal.js"; +import { ProcessorDependencies } from "../types/processor.types.js"; import { RoleGrantedHandler } from "./handlers/roleGranted.handler.js"; export class RegistryHandlerFactory implements IEventHandlerFactory<"Registry", RegistryEvent> { public createHandler( event: ProtocolEvent<"Registry", RegistryEvent>, - pricingProvider: IPricingProvider, - metadataProvider: IMetadataProvider, - repositories: { - project: IProjectReadRepository; - }, - viemProvider: PublicClient, + chainId: ChainId, + dependencies: ProcessorDependencies, ): IEventHandler { if (isRoleGranted(event)) { - return new RoleGrantedHandler( - event, - pricingProvider, - metadataProvider, - repositories.project, - viemProvider, - ); + return new RoleGrantedHandler(event, chainId, dependencies); } throw new UnsupportedEventException("Registry", event.eventName as string); } diff --git a/packages/processors/src/types/index.ts b/packages/processors/src/types/index.ts new file mode 100644 index 0000000..76137fc --- /dev/null +++ b/packages/processors/src/types/index.ts @@ -0,0 +1 @@ +export * from "./processor.types.js"; diff --git a/packages/processors/src/types/processor.types.ts b/packages/processors/src/types/processor.types.ts new file mode 100644 index 0000000..101a7aa --- /dev/null +++ b/packages/processors/src/types/processor.types.ts @@ -0,0 +1,15 @@ +import type { EvmProvider } from "@grants-stack-indexer/chain-providers"; +import type { IMetadataProvider } from "@grants-stack-indexer/metadata"; +import type { IPricingProvider } from "@grants-stack-indexer/pricing"; +import type { + IProjectReadRepository, + IRoundReadRepository, +} from "@grants-stack-indexer/repository"; + +export type ProcessorDependencies = { + evmProvider: EvmProvider; + pricingProvider: IPricingProvider; + metadataProvider: IMetadataProvider; + roundRepository: IRoundReadRepository; + projectRepository: IProjectReadRepository; +}; From 30c3eaf7a05a3972f56bdc881b179c68b2d2e422 Mon Sep 17 00:00:00 2001 From: 0xkenj1 Date: Tue, 22 Oct 2024 12:18:10 -0300 Subject: [PATCH 4/8] feat: tests for handlers --- packages/indexer-client/tsconfig.json | 2 +- .../src/allo/handlers/poolCreated.handler.ts | 2 +- .../processors/src/registry/handlers/index.ts | 2 + .../handlers/profileCreated.hanlder.ts | 113 +++++- .../src/registry/registry.processor.ts | 26 +- .../src/registry/registryProcessorFactory.ts | 25 -- packages/processors/src/schemas/index.ts | 2 + .../processors/src/schemas/projectMetadata.ts | 30 ++ .../schemas.ts => schemas/roundMetadata.ts} | 0 packages/processors/test/index.spec.ts | 5 - .../handlers/profileCreated.handler.spec.ts | 356 ++++++++++++++++++ .../handlers/roleGranted.handler.spec.ts | 125 ++++++ .../test/registry/registry.processor.spec.ts | 75 ++++ 13 files changed, 723 insertions(+), 40 deletions(-) create mode 100644 packages/processors/src/registry/handlers/index.ts delete mode 100644 packages/processors/src/registry/registryProcessorFactory.ts create mode 100644 packages/processors/src/schemas/index.ts create mode 100644 packages/processors/src/schemas/projectMetadata.ts rename packages/processors/src/{helpers/schemas.ts => schemas/roundMetadata.ts} (100%) delete mode 100644 packages/processors/test/index.spec.ts create mode 100644 packages/processors/test/registry/handlers/profileCreated.handler.spec.ts create mode 100644 packages/processors/test/registry/handlers/roleGranted.handler.spec.ts create mode 100644 packages/processors/test/registry/registry.processor.spec.ts diff --git a/packages/indexer-client/tsconfig.json b/packages/indexer-client/tsconfig.json index 66bb87a..21c1c5b 100644 --- a/packages/indexer-client/tsconfig.json +++ b/packages/indexer-client/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../../tsconfig.json", - "include": ["src/**/*"] + "include": ["src/**/*", "test/**/*"] } diff --git a/packages/processors/src/allo/handlers/poolCreated.handler.ts b/packages/processors/src/allo/handlers/poolCreated.handler.ts index 392b587..d876f07 100644 --- a/packages/processors/src/allo/handlers/poolCreated.handler.ts +++ b/packages/processors/src/allo/handlers/poolCreated.handler.ts @@ -6,10 +6,10 @@ import { isAlloNativeToken } from "@grants-stack-indexer/shared"; import type { IEventHandler, ProcessorDependencies, StrategyTimings } from "../../internal.js"; import { getRoundRoles } from "../../helpers/roles.js"; -import { RoundMetadataSchema } from "../../helpers/schemas.js"; import { extractStrategyFromId, getStrategyTimings } from "../../helpers/strategy.js"; import { calculateAmountInUsd } from "../../helpers/tokenMath.js"; import { TokenPriceNotFoundError } from "../../internal.js"; +import { RoundMetadataSchema } from "../../schemas/index.js"; type Dependencies = Pick< ProcessorDependencies, diff --git a/packages/processors/src/registry/handlers/index.ts b/packages/processors/src/registry/handlers/index.ts new file mode 100644 index 0000000..79e1e8b --- /dev/null +++ b/packages/processors/src/registry/handlers/index.ts @@ -0,0 +1,2 @@ +export * from "./profileCreated.hanlder.js"; +export * from "./roleGranted.handler.js"; diff --git a/packages/processors/src/registry/handlers/profileCreated.hanlder.ts b/packages/processors/src/registry/handlers/profileCreated.hanlder.ts index 9e34fdb..c463e0f 100644 --- a/packages/processors/src/registry/handlers/profileCreated.hanlder.ts +++ b/packages/processors/src/registry/handlers/profileCreated.hanlder.ts @@ -1,11 +1,14 @@ -import { Changeset } from "@grants-stack-indexer/repository"; +import { getAddress } from "viem"; + +import { Changeset, ProjectType } from "@grants-stack-indexer/repository"; import { ChainId, ProtocolEvent } from "@grants-stack-indexer/shared"; import { IEventHandler, ProcessorDependencies } from "../../internal.js"; +import { ProjectMetadata, ProjectMetadataSchema } from "../../schemas/projectMetadata.js"; type Dependencies = Pick< ProcessorDependencies, - "projectRepository" | "evmProvider" | "pricingProvider" + "projectRepository" | "evmProvider" | "pricingProvider" | "metadataProvider" >; export class ProfileCreatedHandler implements IEventHandler<"Registry", "ProfileCreated"> { @@ -15,6 +18,110 @@ export class ProfileCreatedHandler implements IEventHandler<"Registry", "Profile private dependencies: Dependencies, ) {} async handle(): Promise { - return []; + const profileId = this.event.params.profileId; + const metadataCid = this.event.params.metadata[1]; + const metadata = await this.dependencies.metadataProvider.getMetadata(metadataCid); + + const parsedMetadata = ProjectMetadataSchema.safeParse(metadata); + + let projectType: ProjectType = "canonical"; + let isProgram = false; + let metadataValue = null; + + if (parsedMetadata.success) { + projectType = this.getProjectTypeFromMetadata(parsedMetadata.data); + isProgram = parsedMetadata.data.type === "program"; + metadataValue = parsedMetadata.data; + } else { + //TODO: Replace with logger + console.warn({ + msg: `ProfileCreated: Failed to parse metadata for profile ${profileId}`, + event: this.event, + metadataCid, + metadata, + }); + } + + const tx = await this.dependencies.evmProvider.getTransaction( + this.event.transactionFields.hash, + ); + + const createdBy = tx.from; + const programTags = isProgram ? ["program"] : []; + + const changes: Changeset[] = [ + { + type: "InsertProject", + args: { + project: { + tags: ["allo-v2", ...programTags], + chainId: this.chainId, + registryAddress: getAddress(this.event.srcAddress), + id: profileId, + name: this.event.params.name, + nonce: this.event.params.nonce, + anchorAddress: getAddress(this.event.params.anchor), + projectNumber: null, + metadataCid: metadataCid, + metadata: metadataValue, + createdByAddress: getAddress(createdBy), + createdAtBlock: BigInt(this.event.blockNumber), + updatedAtBlock: BigInt(this.event.blockNumber), + projectType, + }, + }, + }, + { + type: "InsertProjectRole", + args: { + projectRole: { + chainId: this.chainId, + projectId: this.event.params.profileId, + address: getAddress(this.event.params.owner), + role: "owner", + createdAtBlock: BigInt(this.event.blockNumber), + }, + }, + }, + ]; + + const pendingProjectRoles = + await this.dependencies.projectRepository.getPendingProjectRolesByRole( + this.chainId, + profileId, + ); + + if (pendingProjectRoles.length !== 0) { + for (const role of pendingProjectRoles) { + changes.push({ + type: "InsertProjectRole", + args: { + projectRole: { + chainId: this.chainId, + projectId: profileId, + address: getAddress(role.address), + role: "member", + createdAtBlock: BigInt(this.event.blockNumber), + }, + }, + }); + } + + changes.push({ + type: "DeletePendingProjectRoles", + args: { ids: pendingProjectRoles.map((r) => r.id!) }, + }); + } + + return changes; + } + + private getProjectTypeFromMetadata(metadata: ProjectMetadata): ProjectType { + // if the metadata contains a canonical reference, it's a linked project + if ("canonical" in metadata) { + return "linked"; + } + + return "canonical"; } } diff --git a/packages/processors/src/registry/registry.processor.ts b/packages/processors/src/registry/registry.processor.ts index ce3023e..d96e363 100644 --- a/packages/processors/src/registry/registry.processor.ts +++ b/packages/processors/src/registry/registry.processor.ts @@ -2,17 +2,33 @@ import { Changeset } from "@grants-stack-indexer/repository"; import { ChainId, ProtocolEvent, RegistryEvent } from "@grants-stack-indexer/shared"; import type { IProcessor } from "../internal.js"; +import { UnsupportedEventException } from "../internal.js"; import { ProcessorDependencies } from "../types/processor.types.js"; -import { RegistryHandlerFactory } from "./registryProcessorFactory.js"; +import { ProfileCreatedHandler, RoleGrantedHandler } from "./handlers/index.js"; export class RegistryProcessor implements IProcessor<"Registry", RegistryEvent> { - private factory: RegistryHandlerFactory = new RegistryHandlerFactory(); constructor( private readonly chainId: ChainId, private readonly dependencies: ProcessorDependencies, ) {} - //TODO: Implement - async process(_event: ProtocolEvent<"Registry", RegistryEvent>): Promise { - return await this.factory.createHandler(_event, this.chainId, this.dependencies).handle(); + + async process(event: ProtocolEvent<"Registry", RegistryEvent>): Promise { + //TODO: Implement robust error handling and retry logic + switch (event.eventName) { + case "RoleGranted": + return new RoleGrantedHandler( + event as ProtocolEvent<"Registry", "RoleGranted">, + this.chainId, + this.dependencies, + ).handle(); + case "ProfileCreated": + return new ProfileCreatedHandler( + event as ProtocolEvent<"Registry", "ProfileCreated">, + this.chainId, + this.dependencies, + ).handle(); + default: + throw new UnsupportedEventException("Registry", event.eventName); + } } } diff --git a/packages/processors/src/registry/registryProcessorFactory.ts b/packages/processors/src/registry/registryProcessorFactory.ts deleted file mode 100644 index b92e7bd..0000000 --- a/packages/processors/src/registry/registryProcessorFactory.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ChainId, ProtocolEvent, RegistryEvent } from "@grants-stack-indexer/shared"; - -import { UnsupportedEventException } from "../exceptions/unsupportedEvent.exception.js"; -import { IEventHandler, IEventHandlerFactory } from "../internal.js"; -import { ProcessorDependencies } from "../types/processor.types.js"; -import { RoleGrantedHandler } from "./handlers/roleGranted.handler.js"; - -export class RegistryHandlerFactory implements IEventHandlerFactory<"Registry", RegistryEvent> { - public createHandler( - event: ProtocolEvent<"Registry", RegistryEvent>, - chainId: ChainId, - dependencies: ProcessorDependencies, - ): IEventHandler<"Registry", RegistryEvent> { - if (isRoleGranted(event)) { - return new RoleGrantedHandler(event, chainId, dependencies); - } - throw new UnsupportedEventException("Registry", event.eventName as string); - } -} - -const isRoleGranted = ( - event: ProtocolEvent<"Registry", RegistryEvent>, -): event is ProtocolEvent<"Registry", "RoleGranted"> => { - return event.eventName === "RoleGranted"; -}; diff --git a/packages/processors/src/schemas/index.ts b/packages/processors/src/schemas/index.ts new file mode 100644 index 0000000..d7789f5 --- /dev/null +++ b/packages/processors/src/schemas/index.ts @@ -0,0 +1,2 @@ +export * from "./projectMetadata.js"; +export * from "./roundMetadata.js"; diff --git a/packages/processors/src/schemas/projectMetadata.ts b/packages/processors/src/schemas/projectMetadata.ts new file mode 100644 index 0000000..1543c31 --- /dev/null +++ b/packages/processors/src/schemas/projectMetadata.ts @@ -0,0 +1,30 @@ +import { z } from "zod"; + +export const ProjectMetadataSchema = z.union([ + z + .object({ + title: z.string(), + description: z.string(), + }) + .passthrough() + .transform((data) => ({ type: "project" as const, ...data })), + z + .object({ + canonical: z.object({ + registryAddress: z.string(), + chainId: z.coerce.number(), + }), + }) + .transform((data) => ({ type: "project" as const, ...data })), + z.object({ + type: z.literal("program"), + name: z.string(), + }), + z + .object({ + name: z.string(), + }) + .transform((data) => ({ type: "program" as const, ...data })), +]); + +export type ProjectMetadata = z.infer; diff --git a/packages/processors/src/helpers/schemas.ts b/packages/processors/src/schemas/roundMetadata.ts similarity index 100% rename from packages/processors/src/helpers/schemas.ts rename to packages/processors/src/schemas/roundMetadata.ts diff --git a/packages/processors/test/index.spec.ts b/packages/processors/test/index.spec.ts deleted file mode 100644 index e383c0a..0000000 --- a/packages/processors/test/index.spec.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { describe, it } from "vitest"; - -describe("dummy", () => { - it.skip("dummy", () => {}); -}); diff --git a/packages/processors/test/registry/handlers/profileCreated.handler.spec.ts b/packages/processors/test/registry/handlers/profileCreated.handler.spec.ts new file mode 100644 index 0000000..d2b1828 --- /dev/null +++ b/packages/processors/test/registry/handlers/profileCreated.handler.spec.ts @@ -0,0 +1,356 @@ +import { getAddress } from "viem"; +import { afterEach, beforeEach, describe, expect, it, Mock, vi } from "vitest"; + +import { EvmProvider } from "@grants-stack-indexer/chain-providers"; +import { IMetadataProvider } from "@grants-stack-indexer/metadata"; +import { IPricingProvider } from "@grants-stack-indexer/pricing"; +import { IProjectReadRepository, IRoundReadRepository } from "@grants-stack-indexer/repository"; +import { Bytes32String, ChainId, ProtocolEvent } from "@grants-stack-indexer/shared"; + +import { ProcessorDependencies } from "../../../src/internal.js"; +import { ProfileCreatedHandler } from "../../../src/registry/handlers/index.js"; + +describe("ProfileCreatedHandler", () => { + let mockEvent: ProtocolEvent<"Registry", "ProfileCreated">; + let mockChainId: ChainId; + let mockDependencies: ProcessorDependencies; + const mockedTxHash = "0x6e5a7115323ac1712f7c27adff46df2216324a4ad615a8c9ce488c32a1f3a035"; + const mockedAddress = "0x48f33AE41E1762e1688125C4f1C536B1491dF803"; + + beforeEach(() => { + mockEvent = { + blockTimestamp: 123123123, + chainId: 10 as ChainId, + contractName: "Registry", + eventName: "ProfileCreated", + logIndex: 10, + srcAddress: mockedAddress, + transactionFields: { + hash: mockedTxHash, + transactionIndex: 10, + }, + blockNumber: 123, + params: { + profileId: "0x1231231234" as Bytes32String, + metadata: [1n, "cid-metadata"], + name: "Test Project", + nonce: 1n, + anchor: mockedAddress, + owner: mockedAddress, + }, + } as ProtocolEvent<"Registry", "ProfileCreated">; + + mockChainId = 10 as ChainId; + + mockDependencies = { + projectRepository: { + getPendingProjectRolesByRole: vi.fn().mockResolvedValue([]), + } as unknown as IProjectReadRepository, + evmProvider: { + getTransaction: vi.fn().mockResolvedValue({ from: mockedAddress }), + } as unknown as EvmProvider, + pricingProvider: { + getTokenPrice: vi.fn(), + } as unknown as IPricingProvider, + metadataProvider: { + getMetadata: vi.fn(), + } as unknown as IMetadataProvider, + roundRepository: {} as unknown as IRoundReadRepository, + }; + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + it("handles ProfileCreated event and return the correct changeset", async () => { + (mockDependencies.metadataProvider.getMetadata as Mock).mockResolvedValueOnce({ + type: "program", + name: "Test Project", + }); + const profileCreatedHandler = new ProfileCreatedHandler( + mockEvent, + mockChainId, + mockDependencies, + ); + + const result = await profileCreatedHandler.handle(); + + expect(result).toEqual([ + { + type: "InsertProject", + args: { + project: { + tags: ["allo-v2", "program"], + chainId: mockChainId, + registryAddress: mockEvent.srcAddress, + id: mockEvent.params.profileId, + name: "Test Project", + nonce: 1n, + anchorAddress: mockEvent.params.anchor, + projectNumber: null, + metadataCid: mockEvent.params.metadata[1], + metadata: { type: "program", name: "Test Project" }, + createdByAddress: mockEvent.srcAddress, + createdAtBlock: BigInt(123), + updatedAtBlock: BigInt(123), + projectType: "canonical", + }, + }, + }, + { + type: "InsertProjectRole", + args: { + projectRole: { + chainId: mockChainId, + projectId: mockEvent.params.profileId, + address: mockEvent.params.owner, + role: "owner", + createdAtBlock: BigInt(123), + }, + }, + }, + ]); + + expect( + mockDependencies.projectRepository.getPendingProjectRolesByRole, + ).toHaveBeenCalledWith(mockChainId, mockEvent.params.profileId); + expect(mockDependencies.evmProvider.getTransaction).toHaveBeenCalledWith( + mockEvent.transactionFields.hash, + ); + expect(mockDependencies.metadataProvider.getMetadata).toHaveBeenCalledWith( + mockEvent.params.metadata[1], + ); + }); + + it("logs a warning if metadata parsing fails", async () => { + (mockDependencies.metadataProvider.getMetadata as Mock).mockResolvedValueOnce({ + invalid: "data", + }); + + const handler = new ProfileCreatedHandler(mockEvent, mockChainId, mockDependencies); + const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); + + await handler.handle(); + + expect(consoleWarnSpy).toHaveBeenCalledWith({ + msg: `ProfileCreated: Failed to parse metadata for profile ${mockEvent.params.profileId}`, + event: mockEvent, + metadataCid: "cid-metadata", + metadata: { invalid: "data" }, + }); + }); + + it("returns an null metadata on changeset parsing fails", async () => { + (mockDependencies.metadataProvider.getMetadata as Mock).mockResolvedValueOnce({ + invalid: "data", + }); + + const handler = new ProfileCreatedHandler(mockEvent, mockChainId, mockDependencies); + + const result = await handler.handle(); + + expect(mockDependencies.metadataProvider.getMetadata).toHaveBeenCalledWith( + mockEvent.params.metadata[1], + ); + expect(mockDependencies.evmProvider.getTransaction).toHaveBeenCalledWith( + mockEvent.transactionFields.hash, + ); + expect(result).toEqual([ + { + type: "InsertProject", + args: { + project: { + tags: ["allo-v2"], + chainId: mockChainId, + registryAddress: mockEvent.srcAddress, + id: mockEvent.params.profileId, + name: "Test Project", + nonce: 1n, + anchorAddress: mockEvent.params.anchor, + projectNumber: null, + metadataCid: mockEvent.params.metadata[1], + metadata: null, + createdByAddress: mockEvent.srcAddress, + createdAtBlock: BigInt(123), + updatedAtBlock: BigInt(123), + projectType: "canonical", + }, + }, + }, + { + type: "InsertProjectRole", + args: { + projectRole: { + chainId: mockChainId, + projectId: mockEvent.params.profileId, + address: mockEvent.params.owner, + role: "owner", + createdAtBlock: BigInt(123), + }, + }, + }, + ]); + }); + + it("includes pending project roles in the changeset", async () => { + ( + mockDependencies.projectRepository.getPendingProjectRolesByRole as Mock + ).mockResolvedValueOnce([{ id: "1", address: mockedAddress }]); + + const handler = new ProfileCreatedHandler(mockEvent, mockChainId, mockDependencies); + const result = await handler.handle(); + + expect(result).toContainEqual({ + type: "InsertProjectRole", + args: { + projectRole: { + chainId: mockChainId, + projectId: mockEvent.params.profileId, + address: getAddress(mockedAddress), + role: "member", + createdAtBlock: BigInt(123), + }, + }, + }); + expect(result).toContainEqual({ + type: "DeletePendingProjectRoles", + args: { ids: ["1"] }, + }); + }); + + it("throws an error if getTransaction fails", async () => { + (mockDependencies.evmProvider.getTransaction as Mock).mockRejectedValueOnce( + new Error("Transaction not found"), + ); + + const handler = new ProfileCreatedHandler(mockEvent, mockChainId, mockDependencies); + + await expect(handler.handle()).rejects.toThrow("Transaction not found"); + expect(mockDependencies.evmProvider.getTransaction).toHaveBeenCalledWith( + mockEvent.transactionFields.hash, + ); + }); + + it("processes valid metadata successfully", async () => { + (mockDependencies.metadataProvider.getMetadata as Mock).mockResolvedValueOnce({ + canonical: { + registryAddress: "0x1234567890abcdef", + chainId: 1, + }, + }); + + const handler = new ProfileCreatedHandler(mockEvent, mockChainId, mockDependencies); + + const result = await handler.handle(); + + expect(result).toContainEqual({ + type: "InsertProject", + args: { + project: { + tags: ["allo-v2"], + chainId: mockChainId, + registryAddress: getAddress(mockEvent.srcAddress), + id: mockEvent.params.profileId, + name: "Test Project", + nonce: 1n, + anchorAddress: getAddress(mockEvent.params.anchor), + projectNumber: null, + metadataCid: mockEvent.params.metadata[1], + metadata: { + type: "project", + canonical: { + registryAddress: "0x1234567890abcdef", + chainId: 1, + }, + }, + createdByAddress: getAddress(mockedAddress), + createdAtBlock: BigInt(mockEvent.blockNumber), + updatedAtBlock: BigInt(mockEvent.blockNumber), + projectType: "linked", // As the metadata contains canonical, it should be "linked" + }, + }, + }); + }); + + it("returns correct changeset without pending roles", async () => { + (mockDependencies.metadataProvider.getMetadata as Mock).mockResolvedValueOnce({ + canonical: { + registryAddress: "0x1234567890abcdef", + chainId: 1, + }, + }); + + ( + mockDependencies.projectRepository.getPendingProjectRolesByRole as Mock + ).mockResolvedValueOnce([]); + + const handler = new ProfileCreatedHandler(mockEvent, mockChainId, mockDependencies); + const result = await handler.handle(); + + expect(result).toEqual([ + { + type: "InsertProject", + args: { + project: { + tags: ["allo-v2"], + chainId: mockChainId, + registryAddress: getAddress(mockEvent.srcAddress), + id: mockEvent.params.profileId, + name: "Test Project", + nonce: 1n, + anchorAddress: getAddress(mockEvent.params.anchor), + projectNumber: null, + metadataCid: mockEvent.params.metadata[1], + metadata: { + type: "project", + canonical: { + registryAddress: "0x1234567890abcdef", + chainId: 1, + }, + }, + createdByAddress: getAddress(mockedAddress), + createdAtBlock: BigInt(mockEvent.blockNumber), + updatedAtBlock: BigInt(mockEvent.blockNumber), + projectType: "linked", // As the metadata contains canonical, it should be "linked" + }, + }, + }, + { + type: "InsertProjectRole", + args: { + projectRole: { + chainId: mockChainId, + projectId: mockEvent.params.profileId, + address: getAddress(mockEvent.params.owner), + role: "owner", + createdAtBlock: BigInt(mockEvent.blockNumber), + }, + }, + }, + ]); + + expect( + mockDependencies.projectRepository.getPendingProjectRolesByRole, + ).toHaveBeenCalledWith(mockChainId, mockEvent.params.profileId); + expect(mockDependencies.metadataProvider.getMetadata).toHaveBeenCalledWith( + mockEvent.params.metadata[1], + ); + expect(mockDependencies.evmProvider.getTransaction).toHaveBeenCalledWith( + mockEvent.transactionFields.hash, + ); + }); + + it("throws when metadata provider fails", async () => { + (mockDependencies.metadataProvider.getMetadata as Mock).mockRejectedValueOnce( + new Error("Failed to fetch metadata"), + ); + + const handler = new ProfileCreatedHandler(mockEvent, mockChainId, mockDependencies); + + await expect(handler.handle()).rejects.toThrow("Failed to fetch metadata"); + expect(mockDependencies.metadataProvider.getMetadata).toHaveBeenCalledWith( + mockEvent.params.metadata[1], + ); + }); +}); diff --git a/packages/processors/test/registry/handlers/roleGranted.handler.spec.ts b/packages/processors/test/registry/handlers/roleGranted.handler.spec.ts new file mode 100644 index 0000000..32b4a32 --- /dev/null +++ b/packages/processors/test/registry/handlers/roleGranted.handler.spec.ts @@ -0,0 +1,125 @@ +import { getAddress, InvalidAddressError } from "viem"; +import { describe, expect, it, vi } from "vitest"; + +import { + ALLO_OWNER_ROLE, + Bytes32String, + ChainId, + ProtocolEvent, +} from "@grants-stack-indexer/shared"; + +import { ProcessorDependencies } from "../../../src/internal.js"; +import { RoleGrantedHandler } from "../../../src/registry/handlers/index.js"; // Adjust path if needed + +describe("RoleGrantedHandler", () => { + const mockProjectRepository = { + getProjectById: vi.fn(), + }; + + const dependencies = { + projectRepository: mockProjectRepository, + } as unknown as ProcessorDependencies; + + const chainId = 10 as ChainId; // Example chainId + const blockNumber = 123456; // Example blockNumber + const mockedAccount = "0x48f33AE41E1762e1688125C4f1C536B1491dF803"; + const mockedSender = "0xc0969723D577D31aB4bdF7e53C540c11298c56AF"; + const mockedEvent: ProtocolEvent<"Registry", "RoleGranted"> = { + blockTimestamp: 123123123, + chainId: 10, + contractName: "Registry", + eventName: "RoleGranted", + params: { + role: ALLO_OWNER_ROLE, + account: mockedAccount, + sender: mockedSender, + }, + blockNumber, + logIndex: 10, + srcAddress: mockedAccount, + transactionFields: { + hash: "0x123", + transactionIndex: 1, + }, + }; + + it("returns an empty array if role is ALLO_OWNER_ROLE", async () => { + const event = mockedEvent; + const handler = new RoleGrantedHandler(event, chainId, dependencies); + + const result = await handler.handle(); + + expect(result).toEqual([]); + }); + + it("returns InsertProjectRole if project exists", async () => { + mockedEvent["params"]["role"] = "0x1231231234" as Bytes32String; + const event = mockedEvent; + + mockProjectRepository.getProjectById.mockResolvedValueOnce({ id: "projectId" }); + + const handler = new RoleGrantedHandler(event, chainId, dependencies); + + const result = await handler.handle(); + + expect(result).toEqual([ + { + type: "InsertProjectRole", + args: { + projectRole: { + chainId: chainId, + projectId: "projectId", + address: getAddress(mockedAccount), + role: "member", + createdAtBlock: BigInt(blockNumber), + }, + }, + }, + ]); + }); + + it("returns InsertPendingProjectRole if project does not exist", async () => { + mockedEvent["params"]["role"] = "0x1231231234" as Bytes32String; + const event = mockedEvent; + + mockProjectRepository.getProjectById.mockResolvedValueOnce(null); + + const handler = new RoleGrantedHandler(event, chainId, dependencies); + + const result = await handler.handle(); + + expect(result).toEqual([ + { + type: "InsertPendingProjectRole", + args: { + pendingProjectRole: { + chainId: chainId, + role: event.params.role.toLowerCase(), + address: getAddress(mockedAccount), + createdAtBlock: BigInt(blockNumber), + }, + }, + }, + ]); + }); + + it("throws an error if getAddress throws an error for an invalid account", async () => { + mockedEvent["params"]["account"] = "0xinvalid-address"; // Invalid account address + const event = mockedEvent; + + const handler = new RoleGrantedHandler(event, chainId, dependencies); + + await expect(handler.handle()).rejects.toThrow(InvalidAddressError); + }); + + it("should throw an error if projectRepository throws an error", async () => { + mockedEvent["params"]["role"] = "0x1231231234" as Bytes32String; + const event = mockedEvent; + + mockProjectRepository.getProjectById.mockRejectedValueOnce(new Error()); + + const handler = new RoleGrantedHandler(event, chainId, dependencies); + + await expect(handler.handle()).rejects.toThrow(Error); + }); +}); diff --git a/packages/processors/test/registry/registry.processor.spec.ts b/packages/processors/test/registry/registry.processor.spec.ts new file mode 100644 index 0000000..bc5a252 --- /dev/null +++ b/packages/processors/test/registry/registry.processor.spec.ts @@ -0,0 +1,75 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; + +import type { ChainId, ProtocolEvent, RegistryEvent } from "@grants-stack-indexer/shared"; + +import { ProcessorDependencies, UnsupportedEventException } from "../../src/internal.js"; +import { ProfileCreatedHandler } from "../../src/registry/handlers/profileCreated.hanlder.js"; +import { RoleGrantedHandler } from "../../src/registry/handlers/roleGranted.handler.js"; +import { RegistryProcessor } from "../../src/registry/registry.processor.js"; + +// Mock the handlers and their handle methods +vi.mock("../../src/registry/handlers/roleGranted.handler.js", () => { + const RoleGrantedHandler = vi.fn(); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + RoleGrantedHandler.prototype.handle = vi.fn(); + return { + RoleGrantedHandler, + }; +}); + +// Mock the handlers and their handle methods +vi.mock("../../src/registry/handlers/profileCreated.handler.js", () => { + const ProfileCreatedHandler = vi.fn(); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + ProfileCreatedHandler.prototype.handle = vi.fn(); + return { + ProfileCreatedHandler, + }; +}); + +describe("RegistryProcessor", () => { + const chainId: ChainId = 10 as ChainId; // Replace with appropriate chainId + const dependencies: ProcessorDependencies = {} as ProcessorDependencies; // Replace with actual dependencies + + afterEach(() => { + vi.resetAllMocks(); + }); + + it("should throw UnsupportedEventException for unsupported events", async () => { + const event: ProtocolEvent<"Registry", RegistryEvent> = { + eventName: "UnsupportedEvent", + } as unknown as ProtocolEvent<"Registry", RegistryEvent>; + + const processor = new RegistryProcessor(chainId, dependencies); + + await expect(processor.process(event)).rejects.toThrow(UnsupportedEventException); + }); + + it("should call ProfileCreatedHandler", async () => { + const event: ProtocolEvent<"Registry", "ProfileCreated"> = { + eventName: "ProfileCreated", + } as ProtocolEvent<"Registry", "ProfileCreated">; + + vi.spyOn(ProfileCreatedHandler.prototype, "handle").mockResolvedValue([]); + + const processor = new RegistryProcessor(chainId, dependencies); + const result = await processor.process(event); + + expect(ProfileCreatedHandler.prototype.handle).toHaveBeenCalled(); + expect(result).toEqual([]); // Check if handle returns [] + }); + + it("should call RoleGrantedHandler", async () => { + const event: ProtocolEvent<"Registry", "RoleGranted"> = { + eventName: "RoleGranted", + } as ProtocolEvent<"Registry", "RoleGranted">; + + vi.spyOn(RoleGrantedHandler.prototype, "handle").mockResolvedValue([]); + + const processor = new RegistryProcessor(chainId, dependencies); + const result = await processor.process(event); + + expect(RoleGrantedHandler.prototype.handle).toHaveBeenCalled(); + expect(result).toEqual([]); // Check if handle returns [] + }); +}); From ac86f6f952da7a541b641ea980548f181049795e Mon Sep 17 00:00:00 2001 From: 0xkenj1 Date: Tue, 22 Oct 2024 12:18:46 -0300 Subject: [PATCH 5/8] fix: envio client test --- packages/indexer-client/test/unit/envioIndexerClient.spec.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/indexer-client/test/unit/envioIndexerClient.spec.ts b/packages/indexer-client/test/unit/envioIndexerClient.spec.ts index 1a9abb8..c15dd90 100644 --- a/packages/indexer-client/test/unit/envioIndexerClient.spec.ts +++ b/packages/indexer-client/test/unit/envioIndexerClient.spec.ts @@ -52,6 +52,10 @@ describe("EnvioIndexerClient", () => { srcAddress: "0x1234567890123456789012345678901234567890", logIndex: 0, params: { contractAddress: "0x1234" }, + transactionFields: { + hash: "0x123", + transactionIndex: 1, + }, }, ]; From fab3bc20fb90f890c9ed343af72c55d3cb576ad3 Mon Sep 17 00:00:00 2001 From: 0xkenj1 Date: Tue, 22 Oct 2024 13:46:25 -0300 Subject: [PATCH 6/8] fix: pr comments --- apps/indexer/src/handlers/Registry.ts | 4 +-- .../processors/src/registry/handlers/index.ts | 2 +- ...d.hanlder.ts => profileCreated.handler.ts} | 26 +++++++++---------- .../registry/handlers/roleGranted.handler.ts | 10 ++++--- .../handlers/roleGranted.handler.spec.ts | 4 +-- .../test/registry/registry.processor.spec.ts | 2 +- packages/shared/src/types/common.ts | 4 ++- 7 files changed, 28 insertions(+), 24 deletions(-) rename packages/processors/src/registry/handlers/{profileCreated.hanlder.ts => profileCreated.handler.ts} (87%) diff --git a/apps/indexer/src/handlers/Registry.ts b/apps/indexer/src/handlers/Registry.ts index f597722..78e19b5 100644 --- a/apps/indexer/src/handlers/Registry.ts +++ b/apps/indexer/src/handlers/Registry.ts @@ -1,10 +1,10 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { Registry } from "../../generated"; -// Handler for ProfileNameUpdated event +// Handler for RoleGranted event Registry.RoleGranted.handler(async ({}) => {}); -// Handler for ProfileOwnerUpdated event +// Handler for RoleRevoked event Registry.RoleRevoked.handler(async ({}) => {}); // Handler for ProfileCreated event diff --git a/packages/processors/src/registry/handlers/index.ts b/packages/processors/src/registry/handlers/index.ts index 79e1e8b..59fea08 100644 --- a/packages/processors/src/registry/handlers/index.ts +++ b/packages/processors/src/registry/handlers/index.ts @@ -1,2 +1,2 @@ -export * from "./profileCreated.hanlder.js"; +export * from "./profileCreated.handler.js"; export * from "./roleGranted.handler.js"; diff --git a/packages/processors/src/registry/handlers/profileCreated.hanlder.ts b/packages/processors/src/registry/handlers/profileCreated.handler.ts similarity index 87% rename from packages/processors/src/registry/handlers/profileCreated.hanlder.ts rename to packages/processors/src/registry/handlers/profileCreated.handler.ts index c463e0f..9d89c83 100644 --- a/packages/processors/src/registry/handlers/profileCreated.hanlder.ts +++ b/packages/processors/src/registry/handlers/profileCreated.handler.ts @@ -8,9 +8,11 @@ import { ProjectMetadata, ProjectMetadataSchema } from "../../schemas/projectMet type Dependencies = Pick< ProcessorDependencies, - "projectRepository" | "evmProvider" | "pricingProvider" | "metadataProvider" + "projectRepository" | "evmProvider" | "metadataProvider" >; - +/** + * Handles the ProfileCreated event for the Registry contract from Allo protocol. + */ export class ProfileCreatedHandler implements IEventHandler<"Registry", "ProfileCreated"> { constructor( readonly event: ProtocolEvent<"Registry", "ProfileCreated">, @@ -18,9 +20,10 @@ export class ProfileCreatedHandler implements IEventHandler<"Registry", "Profile private dependencies: Dependencies, ) {} async handle(): Promise { + const { metadataProvider, evmProvider, projectRepository } = this.dependencies; const profileId = this.event.params.profileId; const metadataCid = this.event.params.metadata[1]; - const metadata = await this.dependencies.metadataProvider.getMetadata(metadataCid); + const metadata = await metadataProvider.getMetadata(metadataCid); const parsedMetadata = ProjectMetadataSchema.safeParse(metadata); @@ -42,11 +45,9 @@ export class ProfileCreatedHandler implements IEventHandler<"Registry", "Profile }); } - const tx = await this.dependencies.evmProvider.getTransaction( - this.event.transactionFields.hash, - ); - - const createdBy = tx.from; + const createdBy = + this.event.transactionFields.from ?? + (await evmProvider.getTransaction(this.event.transactionFields.hash)).from; const programTags = isProgram ? ["program"] : []; const changes: Changeset[] = [ @@ -85,11 +86,10 @@ export class ProfileCreatedHandler implements IEventHandler<"Registry", "Profile }, ]; - const pendingProjectRoles = - await this.dependencies.projectRepository.getPendingProjectRolesByRole( - this.chainId, - profileId, - ); + const pendingProjectRoles = await projectRepository.getPendingProjectRolesByRole( + this.chainId, + profileId, + ); if (pendingProjectRoles.length !== 0) { for (const role of pendingProjectRoles) { diff --git a/packages/processors/src/registry/handlers/roleGranted.handler.ts b/packages/processors/src/registry/handlers/roleGranted.handler.ts index f035040..5c246c8 100644 --- a/packages/processors/src/registry/handlers/roleGranted.handler.ts +++ b/packages/processors/src/registry/handlers/roleGranted.handler.ts @@ -6,6 +6,9 @@ import { ALLO_OWNER_ROLE, ChainId, ProtocolEvent } from "@grants-stack-indexer/s import { IEventHandler } from "../../internal.js"; import { ProcessorDependencies } from "../../types/processor.types.js"; +/** + * Handles the RoleGranted event for the Registry contract from Allo protocol. + */ export class RoleGrantedHandler implements IEventHandler<"Registry", "RoleGranted"> { constructor( readonly event: ProtocolEvent<"Registry", "RoleGranted">, @@ -13,16 +16,15 @@ export class RoleGrantedHandler implements IEventHandler<"Registry", "RoleGrante private readonly dependencies: ProcessorDependencies, ) {} async handle(): Promise { + const { projectRepository } = this.dependencies; const role = this.event.params.role.toLocaleLowerCase(); if (role === ALLO_OWNER_ROLE) { return []; } const account = getAddress(this.event.params.account); - const project = await this.dependencies.projectRepository.getProjectById( - this.chainId, - role, - ); + const project = await projectRepository.getProjectById(this.chainId, role); + // The member role for an Allo V2 profile, is the profileId itself. // If a project exists with that id, we create the member role // If it doesn't exists we create a pending project role. This can happens diff --git a/packages/processors/test/registry/handlers/roleGranted.handler.spec.ts b/packages/processors/test/registry/handlers/roleGranted.handler.spec.ts index 32b4a32..30f460a 100644 --- a/packages/processors/test/registry/handlers/roleGranted.handler.spec.ts +++ b/packages/processors/test/registry/handlers/roleGranted.handler.spec.ts @@ -82,7 +82,7 @@ describe("RoleGrantedHandler", () => { mockedEvent["params"]["role"] = "0x1231231234" as Bytes32String; const event = mockedEvent; - mockProjectRepository.getProjectById.mockResolvedValueOnce(null); + mockProjectRepository.getProjectById.mockResolvedValueOnce(undefined); const handler = new RoleGrantedHandler(event, chainId, dependencies); @@ -112,7 +112,7 @@ describe("RoleGrantedHandler", () => { await expect(handler.handle()).rejects.toThrow(InvalidAddressError); }); - it("should throw an error if projectRepository throws an error", async () => { + it("throws an error if projectRepository throws an error", async () => { mockedEvent["params"]["role"] = "0x1231231234" as Bytes32String; const event = mockedEvent; diff --git a/packages/processors/test/registry/registry.processor.spec.ts b/packages/processors/test/registry/registry.processor.spec.ts index bc5a252..06dc730 100644 --- a/packages/processors/test/registry/registry.processor.spec.ts +++ b/packages/processors/test/registry/registry.processor.spec.ts @@ -3,7 +3,7 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import type { ChainId, ProtocolEvent, RegistryEvent } from "@grants-stack-indexer/shared"; import { ProcessorDependencies, UnsupportedEventException } from "../../src/internal.js"; -import { ProfileCreatedHandler } from "../../src/registry/handlers/profileCreated.hanlder.js"; +import { ProfileCreatedHandler } from "../../src/registry/handlers/profileCreated.handler.js"; import { RoleGrantedHandler } from "../../src/registry/handlers/roleGranted.handler.js"; import { RegistryProcessor } from "../../src/registry/registry.processor.js"; diff --git a/packages/shared/src/types/common.ts b/packages/shared/src/types/common.ts index ca5e528..4bd7ee2 100644 --- a/packages/shared/src/types/common.ts +++ b/packages/shared/src/types/common.ts @@ -1,5 +1,7 @@ +import { Hex } from "viem"; + import { Branded } from "../internal.js"; export type ChainId = Branded; -export type Bytes32String = Branded<`0x${string}`, "Bytes32String">; +export type Bytes32String = Branded; From 41cd0886cdfa8b977847010b1c79558f4989f2ec Mon Sep 17 00:00:00 2001 From: 0xkenj1 Date: Wed, 23 Oct 2024 13:32:24 -0300 Subject: [PATCH 7/8] fix: pr comments --- apps/indexer/package.json | 2 +- apps/indexer/pnpm-lock.yaml | 42 +++++++++---------- .../registry/handlers/roleGranted.handler.ts | 6 +-- .../test/registry/registry.processor.spec.ts | 2 +- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/apps/indexer/package.json b/apps/indexer/package.json index 180a03c..c47c442 100644 --- a/apps/indexer/package.json +++ b/apps/indexer/package.json @@ -13,7 +13,7 @@ }, "dependencies": { "chai": "4.3.10", - "envio": "2.4.1", + "envio": "2.5.2", "ethers": "6.8.0", "yaml": "2.5.1" }, diff --git a/apps/indexer/pnpm-lock.yaml b/apps/indexer/pnpm-lock.yaml index c60513e..f000935 100644 --- a/apps/indexer/pnpm-lock.yaml +++ b/apps/indexer/pnpm-lock.yaml @@ -11,8 +11,8 @@ importers: specifier: 4.3.10 version: 4.3.10 envio: - specifier: 2.4.1 - version: 2.4.1 + specifier: 2.5.2 + version: 2.5.2 ethers: specifier: 6.8.0 version: 6.8.0 @@ -895,42 +895,42 @@ packages: integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==, } - envio-darwin-arm64@2.4.1: + envio-darwin-arm64@2.5.2: resolution: { - integrity: sha512-uwwKhThFH9dZhQKd6kmleeTyl//GlJGvhFXuhuJ7xn93oGbOOy3hsoYfJZSbVmb0SSSJkgWdAKN82mYHNfdVwQ==, + integrity: sha512-O/+sgImwhQJXlaypMgESWRUzVlkA3dH+2JpdI1LcIVuCpRN/OvBUbRxDHOU79uxeGXvUeeCn1zSGukod6hxG7g==, } cpu: [arm64] os: [darwin] - envio-darwin-x64@2.4.1: + envio-darwin-x64@2.5.2: resolution: { - integrity: sha512-SKqI2hQmjmqlDYCwFaEK6TDJMLL/UE/wWlUIzsAY5drJQTwAL0mBXzUfQM8JbMgDoxeWyKNegUV1vCuTmCDREw==, + integrity: sha512-/K1XCoIXnjTG4LWUqUUPpSdIBFC1tsryQLp1c040YNnsqVmE9EJufclUL0dc2Rf8SNqC/XgNPGWlndcBDSJWFw==, } cpu: [x64] os: [darwin] - envio-linux-arm64@2.4.1: + envio-linux-arm64@2.5.2: resolution: { - integrity: sha512-ujUUifyKM4kHkHfcudeH8iIf5xPiW9t+IzMCgY4gug2fs3Z3biyhmeaylYNpvNiIoQ58vZqJ23fBKaUn7CPPog==, + integrity: sha512-OqrxwwdfN0mcipnq8TQYQL9yZHoHgr7QB8tILjkjtRhz9LOhyjjqlwmNKiDjbpQ8nR2odORKvTlJGEvHb2AO5A==, } cpu: [arm64] os: [linux] - envio-linux-x64@2.4.1: + envio-linux-x64@2.5.2: resolution: { - integrity: sha512-aeVQ9I4zum6X+kBeJrFev/bTDePZRSlt6HytcnqJd7+iWagQggUejhcrSccp2eRSeQ95sGp+F+lQOFfWJHyEJQ==, + integrity: sha512-qpJDjNeY8b8o1v47ulQgu0RGiN8T3BTg2veCRQHeHgpBqh1f7otPEqX2apADo5K+wYlUsH9HXJfbf25SLEQBdw==, } cpu: [x64] os: [linux] - envio@2.4.1: + envio@2.5.2: resolution: { - integrity: sha512-GsHv4S/mAzr//XCyTHFBnfd1CZFlfaG+wcWJkpeguzhnttNSOMJXCR4UPG0PxV2cZYiA3eVrOSPn58fTzk2G5g==, + integrity: sha512-fWHmggBijehdtOkSErHgASUiEhPwDi4ouxOdgHsi0B/DkabgwTYwa8Z+pBhf324exHhGRhAVxZwBtoKI98MUGA==, } hasBin: true @@ -2956,24 +2956,24 @@ snapshots: dependencies: once: 1.4.0 - envio-darwin-arm64@2.4.1: + envio-darwin-arm64@2.5.2: optional: true - envio-darwin-x64@2.4.1: + envio-darwin-x64@2.5.2: optional: true - envio-linux-arm64@2.4.1: + envio-linux-arm64@2.5.2: optional: true - envio-linux-x64@2.4.1: + envio-linux-x64@2.5.2: optional: true - envio@2.4.1: + envio@2.5.2: optionalDependencies: - envio-darwin-arm64: 2.4.1 - envio-darwin-x64: 2.4.1 - envio-linux-arm64: 2.4.1 - envio-linux-x64: 2.4.1 + envio-darwin-arm64: 2.5.2 + envio-darwin-x64: 2.5.2 + envio-linux-arm64: 2.5.2 + envio-linux-x64: 2.5.2 es-define-property@1.0.0: dependencies: diff --git a/packages/processors/src/registry/handlers/roleGranted.handler.ts b/packages/processors/src/registry/handlers/roleGranted.handler.ts index 5c246c8..1e8c22e 100644 --- a/packages/processors/src/registry/handlers/roleGranted.handler.ts +++ b/packages/processors/src/registry/handlers/roleGranted.handler.ts @@ -17,7 +17,7 @@ export class RoleGrantedHandler implements IEventHandler<"Registry", "RoleGrante ) {} async handle(): Promise { const { projectRepository } = this.dependencies; - const role = this.event.params.role.toLocaleLowerCase(); + const role = this.event.params.role.toLowerCase(); if (role === ALLO_OWNER_ROLE) { return []; } @@ -26,8 +26,8 @@ export class RoleGrantedHandler implements IEventHandler<"Registry", "RoleGrante const project = await projectRepository.getProjectById(this.chainId, role); // The member role for an Allo V2 profile, is the profileId itself. - // If a project exists with that id, we create the member role - // If it doesn't exists we create a pending project role. This can happens + // If a project exist with that id, we create the member role + // If it doesn't exist we create a pending project role. This can happen // when a new project is created, since in Allo V2 the RoleGranted event for a member is // emitted before the ProfileCreated event. if (project) { diff --git a/packages/processors/test/registry/registry.processor.spec.ts b/packages/processors/test/registry/registry.processor.spec.ts index 06dc730..7a8c5cf 100644 --- a/packages/processors/test/registry/registry.processor.spec.ts +++ b/packages/processors/test/registry/registry.processor.spec.ts @@ -35,7 +35,7 @@ describe("RegistryProcessor", () => { vi.resetAllMocks(); }); - it("should throw UnsupportedEventException for unsupported events", async () => { + it("throws UnsupportedEventException for unsupported events", async () => { const event: ProtocolEvent<"Registry", RegistryEvent> = { eventName: "UnsupportedEvent", } as unknown as ProtocolEvent<"Registry", RegistryEvent>; From 38aff32a02b8734460825348ea346b897b41fff1 Mon Sep 17 00:00:00 2001 From: 0xkenj1 Date: Wed, 23 Oct 2024 13:50:37 -0300 Subject: [PATCH 8/8] fix: protocolEvent --- .../test/registry/handlers/roleGranted.handler.spec.ts | 4 ++-- packages/shared/src/types/events/common.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/processors/test/registry/handlers/roleGranted.handler.spec.ts b/packages/processors/test/registry/handlers/roleGranted.handler.spec.ts index 30f460a..030ac33 100644 --- a/packages/processors/test/registry/handlers/roleGranted.handler.spec.ts +++ b/packages/processors/test/registry/handlers/roleGranted.handler.spec.ts @@ -24,7 +24,7 @@ describe("RoleGrantedHandler", () => { const blockNumber = 123456; // Example blockNumber const mockedAccount = "0x48f33AE41E1762e1688125C4f1C536B1491dF803"; const mockedSender = "0xc0969723D577D31aB4bdF7e53C540c11298c56AF"; - const mockedEvent: ProtocolEvent<"Registry", "RoleGranted"> = { + const mockedEvent = { blockTimestamp: 123123123, chainId: 10, contractName: "Registry", @@ -41,7 +41,7 @@ describe("RoleGrantedHandler", () => { hash: "0x123", transactionIndex: 1, }, - }; + } as ProtocolEvent<"Registry", "RoleGranted">; it("returns an empty array if role is ALLO_OWNER_ROLE", async () => { const event = mockedEvent; diff --git a/packages/shared/src/types/events/common.ts b/packages/shared/src/types/events/common.ts index 1dcc640..005cd5b 100644 --- a/packages/shared/src/types/events/common.ts +++ b/packages/shared/src/types/events/common.ts @@ -66,8 +66,8 @@ export type ProtocolEvent - : Record); + : object + : object); /** * TODO: This type is currently only used in the EventsFetcher and IndexerClient.