Skip to content

Commit

Permalink
Merge branch 'dev' into feat/dummy-pricing-provider
Browse files Browse the repository at this point in the history
  • Loading branch information
0xnigir1 committed Nov 13, 2024
2 parents 759c2fb + bd5be45 commit 11d8da6
Show file tree
Hide file tree
Showing 61 changed files with 2,553 additions and 199 deletions.
8 changes: 8 additions & 0 deletions apps/indexer/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ contracts:
- event: RecipientStatusUpdated(uint256 indexed rowIndex, uint256 fullRow, address sender)
name: RecipientStatusUpdatedWithFullRow

# UpdatedRegistration
- event: UpdatedRegistration(address indexed recipientId, bytes data, address sender, uint8 status)
name: UpdatedRegistrationWithStatus
- event: UpdatedRegistration(address indexed recipientId, bytes data, address sender)
name: UpdatedRegistration
- event: UpdatedRegistration(address indexed recipientId, uint256 applicationId, bytes data, address sender, uint8 status)
name: UpdatedRegistrationWithApplicationId

# ########################## ALLO v2.1 ##########################

# # AllocationExtension
Expand Down
5 changes: 5 additions & 0 deletions apps/indexer/src/handlers/Strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,8 @@ Strategy.DirectAllocated.handler(async ({}) => {});
Strategy.RecipientStatusUpdatedWithApplicationId.handler(async ({}) => {});
Strategy.RecipientStatusUpdatedWithRecipientStatus.handler(async ({}) => {});
Strategy.RecipientStatusUpdatedWithFullRow.handler(async ({}) => {});

// UpdatedRegistration Handlers
Strategy.UpdatedRegistrationWithStatus.handler(async ({}) => {});
Strategy.UpdatedRegistration.handler(async ({}) => {});
Strategy.UpdatedRegistrationWithApplicationId.handler(async ({}) => {});
2 changes: 1 addition & 1 deletion packages/data-flow/test/unit/eventsProcessor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ describe("EventsProcessor", () => {
sender: "0x0",
recipientAddress: "0x0",
recipientId: "0x0",
amount: 1n,
amount: "1",
},
transactionFields: {
hash: "0x0",
Expand Down
9 changes: 9 additions & 0 deletions packages/data-flow/test/unit/orchestrator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,15 @@ describe("Orchestrator", { sequential: true }, () => {
AllocatedWithData: "",
AllocatedWithVotes: "",
AllocatedWithStatus: "",
TimestampsUpdatedWithRegistrationAndAllocation: "",
DistributionUpdated: "",
FundsDistributed: "",
RecipientStatusUpdatedWithApplicationId: "",
RecipientStatusUpdatedWithRecipientStatus: "",
RecipientStatusUpdatedWithFullRow: "",
UpdatedRegistrationWithStatus: "",
UpdatedRegistration: "",
UpdatedRegistrationWithApplicationId: "",
};

for (const event of Object.keys(strategyEvents) as StrategyEvent[]) {
Expand Down
1 change: 1 addition & 0 deletions packages/processors/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@grants-stack-indexer/pricing": "workspace:*",
"@grants-stack-indexer/repository": "workspace:*",
"@grants-stack-indexer/shared": "workspace:*",
"statuses-bitmap": "github:gitcoinco/statuses-bitmap#f123d7778e42e16adb98fff2b2ba18c0fee57227",
"viem": "2.21.19",
"zod": "3.23.8"
}
Expand Down
6 changes: 6 additions & 0 deletions packages/processors/src/constants/enums.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export enum ApplicationStatus {
NONE = 0,
PENDING,
APPROVED,
REJECTED,
}
1 change: 1 addition & 0 deletions packages/processors/src/constants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./enums.js";
5 changes: 1 addition & 4 deletions packages/processors/src/exceptions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,5 @@ export * from "./tokenPriceNotFound.exception.js";
export * from "./unsupportedEvent.exception.js";
export * from "./invalidArgument.exception.js";
export * from "./unsupportedStrategy.exception.js";
export * from "./projectNotFound.exception.js";
export * from "./roundNotFound.exception.js";
export * from "./applicationNotFound.exception.js";
export * from "./unknownToken.exception.js";
export * from "./metadataParsingFailed.exception.js";
export * from "./metadataNotFound.exception.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export class MetadataNotFound extends Error {
constructor(message: string) {
super(message);
this.name = "MetadataNotFoundError";
}
}
7 changes: 0 additions & 7 deletions packages/processors/src/exceptions/unknownToken.exception.ts

This file was deleted.

1 change: 1 addition & 0 deletions packages/processors/src/internal.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Types and interfaces
export * from "./types/index.js";
export * from "./interfaces/index.js";
export * from "./constants/index.js";

// Exceptions
export * from "./exceptions/index.js";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { getAddress } from "viem";

import { Changeset } from "@grants-stack-indexer/repository";
import { Changeset, ProjectByRoleNotFound } from "@grants-stack-indexer/repository";
import { ChainId, ProcessorEvent } from "@grants-stack-indexer/shared";

import { IEventHandler, ProcessorDependencies, ProjectByRoleNotFound } from "../../../internal.js";
import { IEventHandler, ProcessorDependencies } from "../../../internal.js";

type Dependencies = Pick<ProcessorDependencies, "projectRepository" | "logger">;
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class BaseDistributedHandler
args: {
chainId: this.chainId,
roundId: round.id,
amount: this.event.params.amount,
amount: BigInt(this.event.params.amount),
},
},
];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { getAddress } from "viem";

import { Changeset } from "@grants-stack-indexer/repository";
import { ChainId, ProcessorEvent } from "@grants-stack-indexer/shared";

import {
IEventHandler,
MetadataNotFound,
MetadataParsingFailed,
ProcessorDependencies,
} from "../../../internal.js";
import { MatchingDistribution, MatchingDistributionSchema } from "../../../schemas/index.js";

type Dependencies = Pick<ProcessorDependencies, "metadataProvider" | "logger">;

/**
* BaseDistributionUpdatedHandler: Processes 'DistributionUpdated' events
*
* - Decodes the updated distribution metadata
* - Creates a changeset to update the round with the new distribution
* - Serves as a base class as all strategies share the same logic for this event.
*
* @dev:
* - Strategy handlers that want to handle the DistributionUpdated event should create an instance of this class corresponding to the event.
*
*/

export class BaseDistributionUpdatedHandler
implements IEventHandler<"Strategy", "DistributionUpdated">
{
constructor(
readonly event: ProcessorEvent<"Strategy", "DistributionUpdated">,
private readonly chainId: ChainId,
private readonly dependencies: Dependencies,
) {}

/* @inheritdoc */
async handle(): Promise<Changeset[]> {
const { logger, metadataProvider } = this.dependencies;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, pointer] = this.event.params.metadata;

const strategyAddress = getAddress(this.event.srcAddress);
const rawDistribution = await metadataProvider.getMetadata<
MatchingDistribution | undefined
>(pointer);

if (!rawDistribution) {
logger.warn(`No matching distribution found for pointer: ${pointer}`);

throw new MetadataNotFound(`No matching distribution found for pointer: ${pointer}`);
}

const distribution = MatchingDistributionSchema.safeParse(rawDistribution);

if (!distribution.success) {
logger.warn(`Failed to parse matching distribution: ${distribution.error.message}`);

throw new MetadataParsingFailed(
`Failed to parse matching distribution: ${distribution.error.message}`,
);
}

return [
{
type: "UpdateRoundByStrategyAddress",
args: {
chainId: this.chainId,
strategyAddress,
round: {
readyForPayoutTransaction: this.event.transactionFields.hash,
matchingDistribution: distribution.data.matchingDistribution,
},
},
},
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { getAddress } from "viem";

import { Changeset } from "@grants-stack-indexer/repository";
import { ChainId, ProcessorEvent } from "@grants-stack-indexer/shared";

import { IEventHandler, ProcessorDependencies } from "../../../internal.js";

type Dependencies = Pick<
ProcessorDependencies,
"roundRepository" | "applicationRepository" | "logger"
>;

/**
* BaseFundsDistributedHandler: Processes 'FundsDistributed' events
*
* - Handles funds distributed events across all strategies.
* - Creates two changesets:
* 1. UpdateApplication: Updates the application with the transaction hash.
* 2. IncrementRoundTotalDistributed: Increments the total distributed amount for a round.
* - Serves as a base class as all strategies share the same logic for this event.
*
* @dev:
* - Strategy handlers that want to handle the FundsDistributed event should create an instance of this class corresponding to the event.
*
*/

export class BaseFundsDistributedHandler implements IEventHandler<"Strategy", "FundsDistributed"> {
constructor(
readonly event: ProcessorEvent<"Strategy", "FundsDistributed">,
private readonly chainId: ChainId,
private readonly dependencies: Dependencies,
) {}

/**
* Handles the FundsDistributed event.
* @throws {RoundNotFound} if the round is not found.
* @throws {ApplicationNotFound} if the application is not found.
* @returns An array of changesets with the following:
* 1. UpdateApplication: Updates the application with the transaction hash.
* 2. IncrementRoundTotalDistributed: Increments the total distributed amount for a round.
*/
async handle(): Promise<Changeset[]> {
const { roundRepository, applicationRepository } = this.dependencies;

const strategyAddress = getAddress(this.event.srcAddress);
const round = await roundRepository.getRoundByStrategyAddressOrThrow(
this.chainId,
strategyAddress,
);

const roundId = round.id;
const anchorAddress = getAddress(this.event.params.recipientId);
const application = await applicationRepository.getApplicationByAnchorAddressOrThrow(
this.chainId,
roundId,
anchorAddress,
);

return [
{
type: "UpdateApplication",
args: {
chainId: this.chainId,
roundId,
applicationId: application.id,
application: {
distributionTransaction: this.event.transactionFields.hash,
},
},
},
{
type: "IncrementRoundTotalDistributed",
args: {
chainId: this.chainId,
roundId: round.id,
amount: BigInt(this.event.params.amount),
},
},
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import StatusesBitmap from "statuses-bitmap";
import { getAddress } from "viem";

import { Application, Changeset } from "@grants-stack-indexer/repository";
import { ChainId, ProcessorEvent } from "@grants-stack-indexer/shared";

import { ApplicationStatus, IEventHandler, ProcessorDependencies } from "../../../internal.js";
import { createStatusUpdate, isValidApplicationStatus } from "../helpers/index.js";

type Dependencies = Pick<
ProcessorDependencies,
"logger" | "roundRepository" | "applicationRepository"
>;

type ApplicationUpdate = {
application: Application;
status: number;
};

/**
* BaseRecipientStatusUpdatedHandler: Processes 'RecipientStatusUpdated' events
*
* - Decodes a bitmap containing status updates for multiple applications
* - Validates each status is valid (between 1-3)
* - Creates changesets to update application statuses in bulk
* - Serves as a base class as all strategies share the same logic for this event
*
* @dev:
* - Strategy handlers that want to handle the RecipientStatusUpdated event should create an instance of this class corresponding to the event.
*
*/
export class BaseRecipientStatusUpdatedHandler
implements IEventHandler<"Strategy", "RecipientStatusUpdatedWithFullRow">
{
private readonly bitmap: StatusesBitmap;

constructor(
readonly event: ProcessorEvent<"Strategy", "RecipientStatusUpdatedWithFullRow">,
private readonly chainId: ChainId,
private readonly dependencies: Dependencies,
) {
this.bitmap = new StatusesBitmap(256n, 4n);
}

/**
* Handles the RecipientStatusUpdated event by processing status updates for multiple applications.
* @returns An array of changesets to update application statuses.
*/
async handle(): Promise<Changeset[]> {
const { roundRepository } = this.dependencies;

const strategyAddress = getAddress(this.event.srcAddress);
const round = await roundRepository.getRoundByStrategyAddressOrThrow(
this.chainId,
strategyAddress,
);

const applicationsToUpdate = await this.getApplicationsToUpdate(round.id);

return applicationsToUpdate.map(({ application, status }) => {
const statusString = ApplicationStatus[status] as Application["status"];
return {
type: "UpdateApplication",
args: {
chainId: this.chainId,
roundId: round.id,
applicationId: application.id,
application: createStatusUpdate({
application,
newStatus: statusString,
blockNumber: this.event.blockNumber,
blockTimestamp: this.event.blockTimestamp,
}),
},
};
});
}

/**
* Gets the list of applications that need to be updated based on the bitmap row
* @param roundId - The ID of the round.
* @returns An array of application updates.
*/
private async getApplicationsToUpdate(roundId: string): Promise<ApplicationUpdate[]> {
const { rowIndex, fullRow } = this.event.params;
this.bitmap.setRow(BigInt(rowIndex), BigInt(fullRow));

const startIndex = BigInt(rowIndex) * BigInt(this.bitmap.itemsPerRow);
const applications: { application: Application; status: number }[] = [];

for (let i = startIndex; i < startIndex + this.bitmap.itemsPerRow; i++) {
const status = this.bitmap.getStatus(i);
if (isValidApplicationStatus(status)) {
const application =
await this.dependencies.applicationRepository.getApplicationById(
i.toString(),
this.chainId,
roundId,
);

if (application) {
applications.push({
application,
status,
});
}
}
}

return applications;
}
}
3 changes: 3 additions & 0 deletions packages/processors/src/processors/strategy/common/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export * from "./baseDistributed.handler.js";
export * from "./base.strategy.js";
export * from "./baseDistributionUpdated.handler.js";
export * from "./baseFundsDistributed.handler.js";
export * from "./baseRecipientStatusUpdated.handler.js";
Loading

0 comments on commit 11d8da6

Please sign in to comment.