Skip to content

Commit 1d83d5d

Browse files
authored
feat: all event handlers for allo contract (#39)
# 🤖 Linear Closes GIT-137 GIT-149 GIT-150 GIT-151 GIT-152 ## Description Added all the remaining handlers for Allo contract events. ## Checklist before requesting a review - [ ] I have conducted a self-review of my code. - [ ] I have conducted a QA. - [ ] If it is a core feature, I have included comprehensive tests.
1 parent 8d06292 commit 1d83d5d

24 files changed

+1231
-132
lines changed

.eslintrc.cjs

+5-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ module.exports = {
2626
"@typescript-eslint/no-unsafe-return": "error",
2727
"@typescript-eslint/no-unused-vars": [
2828
"error",
29-
{ argsIgnorePattern: "_+", ignoreRestSiblings: true },
29+
{
30+
argsIgnorePattern: "_+",
31+
ignoreRestSiblings: true,
32+
destructuredArrayIgnorePattern: "^_",
33+
},
3034
],
3135
"@typescript-eslint/prefer-as-const": "warn",
3236
},

packages/data-flow/test/unit/eventsRegistry.spec.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ describe("InMemoryEventsRegistry", () => {
3030
srcAddress: "0x123",
3131
strategyId: "0xstrategy",
3232
params: {
33-
poolId: 1n,
33+
poolId: "1",
3434
profileId: "0x456",
3535
strategy: "0x789",
3636
token: "0xtoken",
37-
amount: 0n,
38-
metadata: [1n, "0xmetadata"],
37+
amount: "0",
38+
metadata: ["1", "0xmetadata"],
3939
},
4040
transactionFields: {
4141
hash: "0xabc",
@@ -62,12 +62,12 @@ describe("InMemoryEventsRegistry", () => {
6262
srcAddress: "0x123",
6363
strategyId: "0xstrategy",
6464
params: {
65-
poolId: 1n,
65+
poolId: "1",
6666
profileId: "0x456",
6767
strategy: "0x789",
6868
token: "0xtoken",
69-
amount: 0n,
70-
metadata: [1n, "0xmetadata"],
69+
amount: "0",
70+
metadata: ["1", "0xmetadata"],
7171
},
7272
transactionFields: {
7373
hash: "0xabc",

packages/data-flow/test/unit/orchestrator.spec.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,11 @@ describe("Orchestrator", { sequential: true }, () => {
206206
"0x6f9291df02b2664139cec5703c124e4ebce32879c74b6297faa1468aa5ff9ebf" as Hex;
207207
const mockEvent = createMockEvent("Allo", "PoolCreated", 1, {
208208
strategy: strategyAddress,
209-
poolId: 1n,
209+
poolId: "1",
210210
profileId: "0x123",
211211
token: "0x123",
212-
amount: 100n,
213-
metadata: [1n, "1"],
212+
amount: "100",
213+
metadata: ["1", "1"],
214214
});
215215

216216
const eventsProcessorSpy = vi.spyOn(orchestrator["eventsProcessor"], "processEvent");
@@ -396,11 +396,11 @@ describe("Orchestrator", { sequential: true }, () => {
396396
"0x6f9291df02b2664139cec5703c124e4ebce32879c74b6297faa1468aa5ff9ebf" as Hex;
397397
const poolCreatedEvent = createMockEvent("Allo", "PoolCreated", 1, {
398398
strategy: strategyAddress,
399-
poolId: 1n,
399+
poolId: "1",
400400
profileId: "0x123",
401401
token: "0x123",
402-
amount: 100n,
403-
metadata: [1n, "1"],
402+
amount: "100",
403+
metadata: ["1", "1"],
404404
});
405405
const registeredEvent = createMockEvent(
406406
"Strategy",

packages/processors/src/processors/allo/allo.processor.ts

+36-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@ import { AlloEvent, ChainId, ProcessorEvent } from "@grants-stack-indexer/shared
33

44
import type { IProcessor, ProcessorDependencies } from "../../internal.js";
55
import { UnsupportedEventException } from "../../internal.js";
6-
import { PoolCreatedHandler } from "./handlers/index.js";
6+
import {
7+
PoolCreatedHandler,
8+
PoolFundedHandler,
9+
PoolMetadataUpdatedHandler,
10+
RoleGrantedHandler,
11+
RoleRevokedHandler,
12+
} from "./handlers/index.js";
713

814
/**
915
* AlloProcessor handles the processing of Allo V2 events from the Allo contract by delegating them to the appropriate handler
@@ -17,7 +23,35 @@ export class AlloProcessor implements IProcessor<"Allo", AlloEvent> {
1723
async process(event: ProcessorEvent<"Allo", AlloEvent>): Promise<Changeset[]> {
1824
switch (event.eventName) {
1925
case "PoolCreated":
20-
return new PoolCreatedHandler(event, this.chainId, this.dependencies).handle();
26+
return new PoolCreatedHandler(
27+
event as ProcessorEvent<"Allo", "PoolCreated">,
28+
this.chainId,
29+
this.dependencies,
30+
).handle();
31+
case "PoolFunded":
32+
return new PoolFundedHandler(
33+
event as ProcessorEvent<"Allo", "PoolFunded">,
34+
this.chainId,
35+
this.dependencies,
36+
).handle();
37+
case "RoleGranted":
38+
return new RoleGrantedHandler(
39+
event as ProcessorEvent<"Allo", "RoleGranted">,
40+
this.chainId,
41+
this.dependencies,
42+
).handle();
43+
case "PoolMetadataUpdated":
44+
return new PoolMetadataUpdatedHandler(
45+
event as ProcessorEvent<"Allo", "PoolMetadataUpdated">,
46+
this.chainId,
47+
this.dependencies,
48+
).handle();
49+
case "RoleRevoked":
50+
return new RoleRevokedHandler(
51+
event as ProcessorEvent<"Allo", "RoleRevoked">,
52+
this.chainId,
53+
this.dependencies,
54+
).handle();
2155
default:
2256
throw new UnsupportedEventException("Allo", event.eventName);
2357
}
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
export * from "./poolCreated.handler.js";
2+
export * from "./poolFunded.handler.js";
3+
export * from "./poolMetadataUpdated.handler.js";
4+
export * from "./roleGranted.handler.js";
5+
export * from "./roleRevoked.handler.js";

packages/processors/src/processors/allo/handlers/poolCreated.handler.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ export class PoolCreatedHandler implements IEventHandler<"Allo", "PoolCreated">
3939
poolId,
4040
token: tokenAddress,
4141
strategy: strategyAddress,
42-
amount: fundedAmount,
42+
amount,
4343
} = this.event.params;
44+
const fundedAmount = BigInt(amount);
4445
const { hash: txHash, from: txFrom } = this.event.transactionFields;
4546
const strategyId = this.event.strategyId;
4647

@@ -99,7 +100,7 @@ export class PoolCreatedHandler implements IEventHandler<"Allo", "PoolCreated">
99100
// transaction sender
100101
const createdBy = txFrom ?? (await evmProvider.getTransaction(txHash)).from;
101102

102-
const roundRoles = getRoundRoles(poolId);
103+
const roundRoles = getRoundRoles(BigInt(poolId));
103104

104105
const newRound: NewRound = {
105106
chainId: this.chainId,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import type { Changeset } from "@grants-stack-indexer/repository";
2+
import type { ChainId, ProcessorEvent } from "@grants-stack-indexer/shared";
3+
import { getToken, UnknownToken } from "@grants-stack-indexer/shared";
4+
5+
import type { IEventHandler, ProcessorDependencies } from "../../../internal.js";
6+
import { getTokenAmountInUsd } from "../../../helpers/index.js";
7+
8+
type Dependencies = Pick<ProcessorDependencies, "roundRepository" | "logger" | "pricingProvider">;
9+
10+
/**
11+
* Handles the PoolFunded event for the Allo protocol.
12+
*
13+
* This handler performs the following core actions when a pool is funded:
14+
* - Fetches the round metadata from the metadata provider.
15+
* - Returns the changeset to update the round with the new metadata.
16+
*/
17+
export class PoolFundedHandler implements IEventHandler<"Allo", "PoolFunded"> {
18+
constructor(
19+
readonly event: ProcessorEvent<"Allo", "PoolFunded">,
20+
private readonly chainId: ChainId,
21+
private readonly dependencies: Dependencies,
22+
) {}
23+
/* @inheritdoc */
24+
async handle(): Promise<Changeset[]> {
25+
const poolId = this.event.params.poolId.toString();
26+
const fundedAmount = BigInt(this.event.params.amount);
27+
const { roundRepository, pricingProvider } = this.dependencies;
28+
29+
const round = await roundRepository.getRoundByIdOrThrow(this.chainId, poolId);
30+
31+
const token = getToken(this.chainId, round.matchTokenAddress);
32+
33+
//TODO: Review this on Advace Recovery Milestone
34+
if (!token) throw new UnknownToken(round.matchTokenAddress, this.chainId);
35+
36+
const { amountInUsd } = await getTokenAmountInUsd(
37+
pricingProvider,
38+
token,
39+
fundedAmount,
40+
this.event.blockTimestamp,
41+
);
42+
43+
return [
44+
{
45+
type: "IncrementRoundFundedAmount",
46+
args: {
47+
chainId: this.chainId,
48+
roundId: round.id,
49+
fundedAmount,
50+
fundedAmountInUsd: amountInUsd,
51+
},
52+
},
53+
];
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { parseUnits } from "viem";
2+
3+
import type { Changeset } from "@grants-stack-indexer/repository";
4+
import type { ChainId, ProcessorEvent } from "@grants-stack-indexer/shared";
5+
import { getToken } from "@grants-stack-indexer/shared";
6+
7+
import type { IEventHandler, ProcessorDependencies } from "../../../internal.js";
8+
import { getTokenAmountInUsd } from "../../../helpers/index.js";
9+
import { RoundMetadataSchema } from "../../../schemas/index.js";
10+
11+
type Dependencies = Pick<
12+
ProcessorDependencies,
13+
"metadataProvider" | "roundRepository" | "pricingProvider"
14+
>;
15+
16+
/**
17+
* Handles the PoolMetadataUpdated event for the Allo protocol.
18+
*
19+
* This handler performs the following core actions when a pool metadata is updated:
20+
* - Fetches the round metadata from the metadata provider.
21+
* - Returns the changeset to update the round with the new metadata.
22+
*/
23+
export class PoolMetadataUpdatedHandler implements IEventHandler<"Allo", "PoolMetadataUpdated"> {
24+
constructor(
25+
readonly event: ProcessorEvent<"Allo", "PoolMetadataUpdated">,
26+
private readonly chainId: ChainId,
27+
private readonly dependencies: Dependencies,
28+
) {}
29+
/* @inheritdoc */
30+
async handle(): Promise<Changeset[]> {
31+
const [_protocol, metadataPointer] = this.event.params.metadata;
32+
const { metadataProvider, pricingProvider, roundRepository } = this.dependencies;
33+
34+
const metadata = await metadataProvider.getMetadata<{
35+
round?: unknown;
36+
application?: unknown;
37+
}>(metadataPointer);
38+
39+
const round = await roundRepository.getRoundByIdOrThrow(
40+
this.chainId,
41+
this.event.params.poolId.toString(),
42+
);
43+
44+
let matchAmount = round.matchAmount;
45+
let matchAmountInUsd = round.matchAmountInUsd;
46+
47+
const parsedRoundMetadata = RoundMetadataSchema.safeParse(metadata?.round);
48+
const token = getToken(this.chainId, round.matchTokenAddress);
49+
50+
if (parsedRoundMetadata.success && token) {
51+
matchAmount = parseUnits(
52+
parsedRoundMetadata.data.quadraticFundingConfig.matchingFundsAvailable.toString(),
53+
token.decimals,
54+
);
55+
matchAmountInUsd = (
56+
await getTokenAmountInUsd(
57+
pricingProvider,
58+
token,
59+
matchAmount,
60+
this.event.blockTimestamp,
61+
)
62+
).amountInUsd;
63+
}
64+
65+
return [
66+
{
67+
type: "UpdateRound",
68+
args: {
69+
chainId: this.chainId,
70+
roundId: this.event.params.poolId.toString(),
71+
round: {
72+
matchAmount,
73+
matchAmountInUsd,
74+
applicationMetadataCid: metadataPointer,
75+
applicationMetadata: metadata?.application ?? {},
76+
roundMetadataCid: metadataPointer,
77+
roundMetadata: metadata?.round ?? {},
78+
},
79+
},
80+
},
81+
];
82+
}
83+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { getAddress } from "viem";
2+
3+
import type { Changeset, Round } from "@grants-stack-indexer/repository";
4+
import type { ChainId, ProcessorEvent } from "@grants-stack-indexer/shared";
5+
6+
import type { IEventHandler, ProcessorDependencies } from "../../../internal.js";
7+
8+
type Dependencies = Pick<ProcessorDependencies, "roundRepository">;
9+
10+
/**
11+
* Handles the RoleGranted event for the Allo protocol.
12+
*
13+
* This handler performs the following core actions when a new role is granted:
14+
* - Insert a new round role if the role granted is admin or manager.
15+
* - Insert a new pending round role if the role granted is not admin or manager.
16+
* - Return the changeset.
17+
*/
18+
export class RoleGrantedHandler implements IEventHandler<"Allo", "RoleGranted"> {
19+
constructor(
20+
readonly event: ProcessorEvent<"Allo", "RoleGranted">,
21+
private readonly chainId: ChainId,
22+
private readonly dependencies: Dependencies,
23+
) {}
24+
/* @inheritdoc */
25+
async handle(): Promise<Changeset[]> {
26+
const role = this.event.params.role.toLowerCase();
27+
const account = getAddress(this.event.params.account);
28+
const { roundRepository } = this.dependencies;
29+
30+
let round: Round | undefined = undefined;
31+
32+
// search for a round where the admin role is the role granted
33+
round = await roundRepository.getRoundByRole(this.chainId, "admin", role);
34+
if (round) {
35+
return [
36+
{
37+
type: "InsertRoundRole",
38+
args: {
39+
roundRole: {
40+
chainId: this.chainId,
41+
roundId: round.id,
42+
role: "admin",
43+
address: account,
44+
createdAtBlock: BigInt(this.event.blockNumber),
45+
},
46+
},
47+
},
48+
];
49+
}
50+
51+
// search for a round where the manager role is the role granted
52+
round = await roundRepository.getRoundByRole(this.chainId, "manager", role);
53+
if (round) {
54+
return [
55+
{
56+
type: "InsertRoundRole",
57+
args: {
58+
roundRole: {
59+
chainId: this.chainId,
60+
roundId: round.id,
61+
role: "manager",
62+
address: account,
63+
createdAtBlock: BigInt(this.event.blockNumber),
64+
},
65+
},
66+
},
67+
];
68+
}
69+
70+
return [
71+
{
72+
type: "InsertPendingRoundRole",
73+
args: {
74+
pendingRoundRole: {
75+
chainId: this.chainId,
76+
role: role,
77+
address: account,
78+
createdAtBlock: BigInt(this.event.blockNumber),
79+
},
80+
},
81+
},
82+
];
83+
}
84+
}

0 commit comments

Comments
 (0)