Skip to content

Commit 6e7eca3

Browse files
authored
feat: production release 🚀 (#384)
1 parent 1107163 commit 6e7eca3

File tree

10 files changed

+213
-2
lines changed

10 files changed

+213
-2
lines changed

‎packages/indexer-api/src/dtos/deposits.dto.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export type DepositReturnType = {
120120
fillGasFeeUsd?: string;
121121
fillGasTokenPriceUsd?: string;
122122
actionsSucceeded?: boolean | null;
123+
actionsTargetChainId?: string;
123124

124125
// from fill
125126
relayer?: string;

‎packages/indexer-api/src/services/deposits.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ const RelayHashInfoFields = [
5656
WHEN rhi.includedActions = true AND rhi.status = 'filled' THEN (rhi.callsFailedEventId IS NULL)
5757
ELSE NULL
5858
END as "actionsSucceeded"`,
59+
`rhi.actionsTargetChainId as "actionsTargetChainId"`,
5960
];
6061

6162
const FilledRelayFields = [

‎packages/indexer-database/src/entities/RelayHashInfo.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ export class RelayHashInfo {
153153
@Column({ nullable: true })
154154
includedActions: boolean;
155155

156+
@Column({ nullable: true, type: "bigint" })
157+
actionsTargetChainId: string;
158+
156159
@UpdateDateColumn()
157160
updatedAt: Date;
158161
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { MigrationInterface, QueryRunner } from "typeorm";
2+
3+
export class RelayHashInfo1758762765600 implements MigrationInterface {
4+
name = "RelayHashInfo1758762765600";
5+
6+
public async up(queryRunner: QueryRunner): Promise<void> {
7+
await queryRunner.query(
8+
`ALTER TABLE "relay_hash_info" ADD "actionsTargetChainId" bigint`,
9+
);
10+
}
11+
12+
public async down(queryRunner: QueryRunner): Promise<void> {
13+
await queryRunner.query(
14+
`ALTER TABLE "relay_hash_info" DROP COLUMN "actionsTargetChainId"`,
15+
);
16+
}
17+
}

‎packages/indexer/src/data-indexing/service/SpokePoolIndexerDataHandler.ts‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { IntegratorIdMessage } from "../../messaging/IntegratorIdWorker";
2626
import { getMaxBlockLookBack } from "../../web3/constants";
2727
import { PriceMessage } from "../../messaging/priceWorker";
2828
import { EventDecoder } from "../../web3/EventDecoder";
29+
import { matchFillEventsWithTargetChainActions } from "../../utils/targetChainActionsUtils";
2930

3031
export type FetchEventsResult = {
3132
v3FundsDepositedEvents: utils.V3FundsDepositedWithIntegradorId[];
@@ -166,6 +167,28 @@ export class SpokePoolIndexerDataHandler implements IndexerDataHandler {
166167
lastFinalisedBlock,
167168
);
168169

170+
// Match fill events with target chain action events
171+
const fillTargetChainActionPairs = matchFillEventsWithTargetChainActions(
172+
storedEvents.fills.map((f) => f.data),
173+
transactionReceipts,
174+
);
175+
176+
if (fillTargetChainActionPairs.length > 0) {
177+
this.logger.debug({
178+
at: "Indexer#SpokePoolIndexerDataHandler#processBlockRange",
179+
message:
180+
"Found fill transactions with target chain action destinations",
181+
count: fillTargetChainActionPairs.length,
182+
pairs: fillTargetChainActionPairs.map((pair) => ({
183+
fillEventId: pair.fill.id,
184+
transactionHash: pair.fill.transactionHash,
185+
destinationChainId: pair.fill.destinationChainId,
186+
outputAmount: pair.fill.outputAmount,
187+
actionsTargetChainId: pair.actionsTargetChainId,
188+
})),
189+
});
190+
}
191+
169192
//FIXME: Remove performance timing
170193
const timeToStoreEvents = performance.now();
171194

@@ -195,6 +218,7 @@ export class SpokePoolIndexerDataHandler implements IndexerDataHandler {
195218
deletedDeposits,
196219
depositSwapPairs,
197220
fillCallsFailedPairs,
221+
fillTargetChainActionPairs,
198222
fillsGasFee,
199223
);
200224

‎packages/indexer/src/data-indexing/service/SvmSpokePoolIndexerDataHandler.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ export class SvmSpokePoolIndexerDataHandler implements IndexerDataHandler {
142142
deletedDeposits,
143143
[], // swapBeforeBridge events
144144
[], // callsFailed events
145+
[], // targetChainAction pairs
145146
fillsGasFee,
146147
);
147148

‎packages/indexer/src/services/spokePoolProcessor.ts‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
DepositSwapPair,
1818
FillCallsFailedPair,
1919
} from "../data-indexing/service/SpokePoolIndexerDataHandler";
20+
import { FillTargetChainActionPair } from "../utils/targetChainActionsUtils";
2021
import { StoreEventsResult } from "../database/SpokePoolRepository";
2122
import { getDbLockKeyForDeposit } from "../utils";
2223

@@ -39,6 +40,7 @@ export class SpokePoolProcessor {
3940
deletedDeposits: entities.V3FundsDeposited[],
4041
depositSwapPairs: DepositSwapPair[],
4142
fillCallsFailedPairs: FillCallsFailedPair[],
43+
fillTargetChainActionPairs: FillTargetChainActionPair[],
4244
fillsGasFee?: Record<string, bigint | undefined>,
4345
) {
4446
// Update relay hash info records related to deleted deposits
@@ -54,6 +56,9 @@ export class SpokePoolProcessor {
5456
});
5557
await this.assignSwapEventToRelayHashInfo(depositSwapPairs);
5658
await this.assignCallsFailedEventToRelayHashInfo(fillCallsFailedPairs);
59+
await this.assignTargetChainActionEventToRelayHashInfo(
60+
fillTargetChainActionPairs,
61+
);
5762
const timeToAssignSpokeEventsToRelayHashInfoEnd = performance.now();
5863

5964
// Update expired deposits
@@ -548,6 +553,28 @@ export class SpokePoolProcessor {
548553
);
549554
}
550555

556+
/**
557+
* Assigns target chain action information to the relay hash info
558+
*/
559+
private async assignTargetChainActionEventToRelayHashInfo(
560+
fillTargetChainActionPairs: FillTargetChainActionPair[],
561+
) {
562+
const relayHashInfoRepository = this.postgres.getRepository(
563+
entities.RelayHashInfo,
564+
);
565+
await Promise.all(
566+
fillTargetChainActionPairs.map((fillTargetChainActionPair) =>
567+
relayHashInfoRepository.update(
568+
{ fillEventId: fillTargetChainActionPair.fill.id },
569+
{
570+
actionsTargetChainId:
571+
fillTargetChainActionPair.actionsTargetChainId,
572+
},
573+
),
574+
),
575+
);
576+
}
577+
551578
private notifyWebhooks(
552579
deposits: entities.V3FundsDeposited[],
553580
fills: entities.FilledV3Relay[],
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { providers } from "ethers";
2+
import { EventDecoder } from "../web3/EventDecoder";
3+
import { entities } from "@repo/indexer-database";
4+
5+
export const TARGET_CHAIN_ACTION_ADDRESSES: Record<string, string> = {
6+
"0x200000000000000000000000000000000000010C": "1337", // HyperCore
7+
};
8+
9+
export interface FillTargetChainActionPair {
10+
fill: entities.FilledV3Relay;
11+
actionsTargetChainId: string;
12+
}
13+
14+
/**
15+
* Matches fill events with Transfer events to target chain action destinations
16+
* @param fills - Array of fill events
17+
* @param transactionReceipts - Map of transaction receipts
18+
* @returns Array of matched fill and target chain action pairs
19+
*/
20+
export function matchFillEventsWithTargetChainActions(
21+
fills: entities.FilledV3Relay[],
22+
transactionReceipts: Record<string, providers.TransactionReceipt>,
23+
): FillTargetChainActionPair[] {
24+
const transactionReceiptsList = Object.values(transactionReceipts);
25+
const targetChainActionEvents = transactionReceiptsList
26+
.map((transactionReceipt) =>
27+
EventDecoder.decodeTransferEvents(transactionReceipt),
28+
)
29+
.flat();
30+
31+
const fillsAndTargetChainActionsByTxHash = targetChainActionEvents.reduce(
32+
(acc, targetChainAction) => {
33+
acc[targetChainAction.transactionHash] = {
34+
fills: fills.filter(
35+
(f) =>
36+
f.transactionHash.toLowerCase() ===
37+
targetChainAction.transactionHash.toLowerCase(),
38+
),
39+
targetChainActions: targetChainActionEvents.filter(
40+
(t) =>
41+
t.transactionHash.toLowerCase() ===
42+
targetChainAction.transactionHash.toLowerCase(),
43+
),
44+
};
45+
return acc;
46+
},
47+
{} as Record<
48+
string,
49+
{
50+
fills: entities.FilledV3Relay[];
51+
targetChainActions: any[];
52+
}
53+
>,
54+
);
55+
56+
// Match fills with their corresponding target chain action events
57+
const fillTargetChainActionPairs: FillTargetChainActionPair[] = Object.values(
58+
fillsAndTargetChainActionsByTxHash,
59+
)
60+
.map((fillAndTargetChainAction) => {
61+
const { fills: txFills, targetChainActions } = fillAndTargetChainAction;
62+
const sortedFills = txFills.sort((a, b) => a.logIndex - b.logIndex);
63+
const sortedTargetChainActions = targetChainActions.sort(
64+
(a, b) => a.logIndex - b.logIndex,
65+
);
66+
const matchedPairs: FillTargetChainActionPair[] = [];
67+
const usedTargetChainActions = new Set<number>(); // Track used target chain actions by their log index
68+
69+
sortedFills.forEach((fill) => {
70+
const matchingTargetChainAction = sortedTargetChainActions.find(
71+
(targetChainAction) =>
72+
targetChainAction.logIndex > fill.logIndex &&
73+
!usedTargetChainActions.has(targetChainAction.logIndex) &&
74+
targetChainAction.args.value.toString() === fill.outputAmount &&
75+
Object.keys(TARGET_CHAIN_ACTION_ADDRESSES).some(
76+
(targetChainAddress) =>
77+
targetChainAction.args.to.toLowerCase() ===
78+
targetChainAddress.toLowerCase(),
79+
),
80+
);
81+
if (matchingTargetChainAction) {
82+
// Get the target chain action chain ID
83+
const targetChainAddress = Object.keys(
84+
TARGET_CHAIN_ACTION_ADDRESSES,
85+
).find(
86+
(addr) =>
87+
addr.toLowerCase() ===
88+
matchingTargetChainAction.args.to.toLowerCase(),
89+
);
90+
const actionsTargetChainId = targetChainAddress
91+
? TARGET_CHAIN_ACTION_ADDRESSES[targetChainAddress]
92+
: null;
93+
94+
if (actionsTargetChainId) {
95+
matchedPairs.push({ fill, actionsTargetChainId });
96+
usedTargetChainActions.add(matchingTargetChainAction.logIndex); // Mark this target chain action as used
97+
}
98+
}
99+
});
100+
101+
return matchedPairs;
102+
})
103+
.flat();
104+
105+
return fillTargetChainActionPairs;
106+
}

‎packages/indexer/src/web3/EventDecoder.ts‎

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { ethers } from "ethers";
22
import { MulticallHandler__factory } from "@across-protocol/contracts";
3-
import { SwapBeforeBridgeEvent, CallsFailedEvent } from "./model/events";
3+
import {
4+
SwapBeforeBridgeEvent,
5+
CallsFailedEvent,
6+
TransferEvent,
7+
} from "./model/events";
48
import {
59
BASE_SWAP_BEFORE_BRIDGE_ABI,
610
SPOKE_POOL_PERIPHERY_SWAP_BEFORE_BRIDGE_ABI,
@@ -47,17 +51,36 @@ export class EventDecoder {
4751
return events;
4852
}
4953

54+
static decodeTransferEvents(receipt: ethers.providers.TransactionReceipt) {
55+
const transferEventTopic =
56+
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
57+
const transferABI = [
58+
"event Transfer(address indexed from, address indexed to, uint256 value)",
59+
];
60+
61+
const events: TransferEvent[] = this.decodeTransactionReceiptLogs(
62+
receipt,
63+
transferEventTopic,
64+
transferABI,
65+
true,
66+
);
67+
68+
return events;
69+
}
70+
5071
static decodeTransactionReceiptLogs(
5172
receipt: ethers.providers.TransactionReceipt,
5273
eventTopic: string,
5374
abi: any,
75+
skipEmptyLogs: boolean = false,
5476
) {
5577
const events: (ethers.providers.Log & { args: any })[] = [];
5678

5779
for (const log of receipt.logs) {
5880
const contractInterface = new ethers.utils.Interface(abi);
5981

60-
if (log.topics.length === 0) continue;
82+
if (log.topics.length === 0 || (log.data === "0x" && skipEmptyLogs))
83+
continue;
6184

6285
try {
6386
const parsedLog = contractInterface.parseLog(log);

‎packages/indexer/src/web3/model/events.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,11 @@ export interface CallsFailedEvent extends providers.Log {
1919
fallbackRecipient: string;
2020
};
2121
}
22+
23+
export interface TransferEvent extends providers.Log {
24+
args: {
25+
from: string;
26+
to: string;
27+
value: BigNumber;
28+
};
29+
}

0 commit comments

Comments
 (0)