Skip to content

Commit 7f45383

Browse files
author
amateima
committed
feat: identify eth deposits
1 parent e44ae65 commit 7f45383

File tree

7 files changed

+1327
-33
lines changed

7 files changed

+1327
-33
lines changed

Diff for: src/modules/scraper/adapter/messaging/BlocksEventsConsumer.ts

+260-33
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,30 @@ import {
3030
RequestedSpeedUpV3DepositEvent,
3131
} from "../../../web3/model";
3232
import SwapAndBridgeAbi from "../../../web3/services/abi/SwapAndBridge.json";
33+
import WethMainnetAbi from "../../../web3/services/abi/WETH_1.json";
34+
import WethOptimismAbi from "../../../web3/services/abi/WETH_10.json";
35+
import WethArbitrumAbi from "../../../web3/services/abi/WETH_42161.json";
36+
import WethLineaAbi from "../../../web3/services/abi/WETH_59144.json";
37+
import WethBaseAbi from "../../../web3/services/abi/WETH_8453.json";
3338
import { SwapBeforeBridgeEvent } from "../../../web3/model/swap-and-bridge-events";
39+
import { ChainIds } from "../../../web3/model/ChainId";
40+
import {
41+
WethDepositEvent,
42+
WethDepositEventBase,
43+
WethDepositEventLinea,
44+
WethDepositEventOptimism,
45+
WethTransfetEventArbitrum,
46+
} from "../../../web3/model/weth-events";
3447
import { AppConfig } from "../../../configuration/configuration.service";
3548
import { splitBlockRanges } from "../../utils";
3649
import { AcrossContractsVersion } from "../../../web3/model/across-version";
3750

3851
const SPOKE_POOL_VERIFIER_CONTRACT_ADDRESS = "0x269727F088F16E1Aea52Cf5a97B1CD41DAA3f02D";
52+
type EnrichedDepositEvent = {
53+
depositEvent: FundsDepositedV3Event;
54+
swapEvent?: SwapBeforeBridgeEvent;
55+
wethEvent?: any;
56+
};
3957

4058
@Processor(ScraperQueue.BlocksEvents)
4159
export class BlocksEventsConsumer {
@@ -204,52 +222,210 @@ export class BlocksEventsConsumer {
204222
const depositEventsByTxHash = typedEvents.reduce((acc, event) => {
205223
acc[event.transactionHash] = [...(acc[event.transactionHash] || []), event];
206224
return acc;
207-
}, {} as Record<string, FundsDepositedV3Event[]>);
208-
const swapEventsByTxHash: Record<string, SwapBeforeBridgeEvent[]> = {};
225+
}, {} as { [txHash: string]: FundsDepositedV3Event[] });
226+
const swapEventsByTxHash = await this.extractSwapEvents(depositEventsByTxHash, chainId);
227+
const wethEventsByTxHash = await this.extractWethEvents(depositEventsByTxHash, chainId);
228+
const matchedEvents = this.matchDepositEventsWithSwapEvents(
229+
depositEventsByTxHash,
230+
swapEventsByTxHash,
231+
wethEventsByTxHash,
232+
chainId,
233+
);
234+
await this.saveDepositEvents(chainId, matchedEvents);
235+
}
209236

210-
for (const transactionHash of Object.keys(depositEventsByTxHash)) {
211-
const txReceipt = await this.providers.getProvider(chainId).getTransactionReceipt(transactionHash);
212-
const swapBeforeBridgeEvents = this.providers.parseTransactionReceiptLogs(
213-
txReceipt,
214-
"SwapBeforeBridge",
215-
SwapAndBridgeAbi,
216-
) as unknown as SwapBeforeBridgeEvent[];
217-
swapEventsByTxHash[transactionHash] = swapBeforeBridgeEvents;
237+
async saveDepositEvents(chainId: number, events: { [txHash: string]: EnrichedDepositEvent[] }) {
238+
for (const txHash of Object.keys(events)) {
239+
for (const matchedEvent of events[txHash]) {
240+
try {
241+
const { depositEvent, swapEvent, wethEvent } = matchedEvent;
242+
const deposit = await this.fromFundsDepositedV3EventToDeposit(chainId, depositEvent, swapEvent, wethEvent);
243+
const result = await this.depositRepository.insert(deposit);
244+
await this.scraperQueuesService.publishMessage<BlockNumberQueueMessage>(ScraperQueue.BlockNumber, {
245+
depositId: result.identifiers[0].id,
246+
});
247+
} catch (error) {
248+
if (error instanceof QueryFailedError && error.driverError?.code === "23505") {
249+
// Ignore duplicate key value violates unique constraint error.
250+
this.logger.warn(error);
251+
} else {
252+
throw error;
253+
}
254+
}
255+
}
218256
}
257+
}
258+
259+
public matchDepositEventsWithSwapEvents(
260+
depositEventsByTxHash: { [txHash: string]: FundsDepositedV3Event[] },
261+
swapEventsByTxHash: { [txHash: string]: SwapBeforeBridgeEvent[] },
262+
wethEventsByTxHash: { [txHash: string]: any[] },
263+
chainId: number,
264+
) {
265+
const events: { [txHash: string]: EnrichedDepositEvent[] } = {};
219266

220267
for (const transactionHash of Object.keys(depositEventsByTxHash)) {
221-
const pairs: [FundsDepositedV3Event, SwapBeforeBridgeEvent | undefined][] = [];
222268
const depositEvents = depositEventsByTxHash[transactionHash].sort((d1, d2) => d1.logIndex - d2.logIndex);
223269
const swapEvents = swapEventsByTxHash[transactionHash].sort((s1, s2) => s1.logIndex - s2.logIndex);
270+
const wethEvents = wethEventsByTxHash[transactionHash].sort((s1, s2) => s1.logIndex - s2.logIndex);
224271

225272
for (const depositEvent of depositEvents) {
273+
const spokePoolAddresses = this.appConfig.values.web3.spokePoolContracts[chainId].map((sp) => sp.address);
274+
const wethEventIndex = wethEvents.findIndex((e) => {
275+
if (chainId === ChainIds.mainnet) {
276+
const typedEvent = e as WethDepositEvent;
277+
return (
278+
e.logIndex < depositEvent.logIndex &&
279+
typedEvent.args.wad.eq(depositEvent.args.inputAmount) &&
280+
spokePoolAddresses.includes(typedEvent.args.dst)
281+
);
282+
} else if (chainId === ChainIds.optimism) {
283+
const typedEvent = e as WethDepositEventOptimism;
284+
return (
285+
e.logIndex < depositEvent.logIndex &&
286+
typedEvent.args.wad.eq(depositEvent.args.inputAmount) &&
287+
spokePoolAddresses.includes(typedEvent.args.dst)
288+
);
289+
} else if (chainId === ChainIds.arbitrum) {
290+
const typedEvent = e as WethTransfetEventArbitrum;
291+
return (
292+
e.logIndex < depositEvent.logIndex &&
293+
typedEvent.args.value.eq(depositEvent.args.inputAmount) &&
294+
spokePoolAddresses.includes(typedEvent.args.to)
295+
);
296+
} else if (chainId === ChainIds.linea) {
297+
const typedEvent = e as WethDepositEventLinea;
298+
return (
299+
e.logIndex < depositEvent.logIndex &&
300+
typedEvent.args.wad.eq(depositEvent.args.inputAmount) &&
301+
spokePoolAddresses.includes(typedEvent.args.dst)
302+
);
303+
} else if (chainId === ChainIds.base) {
304+
const typedEvent = e as WethDepositEventBase;
305+
return (
306+
e.logIndex < depositEvent.logIndex &&
307+
typedEvent.args.wad.eq(depositEvent.args.inputAmount) &&
308+
spokePoolAddresses.includes(typedEvent.args.dst)
309+
);
310+
} else {
311+
throw new Error(`Unkown match criteria for weth event on chainId ${chainId}`);
312+
}
313+
});
314+
226315
const swapEventIndex = swapEvents.findIndex((e) => e.logIndex < depositEvent.logIndex);
316+
events[transactionHash] = [
317+
...(events[transactionHash] || []),
318+
{
319+
depositEvent,
320+
swapEvent: swapEventIndex >= 0 ? swapEvents[swapEventIndex] : undefined,
321+
wethEvent: wethEventIndex >= 0 ? wethEvents[wethEventIndex] : undefined,
322+
},
323+
];
324+
325+
if (wethEventIndex >= 0) {
326+
wethEvents.splice(wethEventIndex, 1);
327+
}
227328

228329
if (swapEventIndex >= 0) {
229-
pairs.push([depositEvent, swapEvents[swapEventIndex]]);
230330
swapEvents.splice(swapEventIndex, 1);
231-
} else {
232-
pairs.push([depositEvent, undefined]);
233331
}
234332
}
333+
}
235334

236-
for (const [depositEvent, swapEvent] of pairs) {
237-
try {
238-
const deposit = await this.fromFundsDepositedV3EventToDeposit(chainId, depositEvent, swapEvent);
239-
const result = await this.depositRepository.insert(deposit);
240-
await this.scraperQueuesService.publishMessage<BlockNumberQueueMessage>(ScraperQueue.BlockNumber, {
241-
depositId: result.identifiers[0].id,
242-
});
243-
} catch (error) {
244-
if (error instanceof QueryFailedError && error.driverError?.code === "23505") {
245-
// Ignore duplicate key value violates unique constraint error.
246-
this.logger.warn(error);
247-
} else {
248-
throw error;
249-
}
250-
}
335+
return events;
336+
}
337+
338+
private async extractSwapEvents(
339+
depositEventsByTxHash: { [txHash: string]: FundsDepositedV3Event[] },
340+
chainId: number,
341+
) {
342+
const swapEventsByTxHash: { [txHash: string]: SwapBeforeBridgeEvent[] } = {};
343+
344+
for (const transactionHash of Object.keys(depositEventsByTxHash)) {
345+
const txReceipt = await this.providers.getProvider(chainId).getTransactionReceipt(transactionHash);
346+
const swapBeforeBridgeEvents = this.providers.parseTransactionReceiptLogs(
347+
txReceipt,
348+
"SwapBeforeBridge",
349+
SwapAndBridgeAbi,
350+
) as unknown as SwapBeforeBridgeEvent[];
351+
swapEventsByTxHash[transactionHash] = swapBeforeBridgeEvents;
352+
}
353+
return swapEventsByTxHash;
354+
}
355+
356+
private async extractWethEvents(
357+
depositEventsByTxHash: { [txHash: string]: FundsDepositedV3Event[] },
358+
chainId: number,
359+
) {
360+
const wethEventsByTxHash: { [txHash: string]: any[] } = {};
361+
const supportedChainIds = [ChainIds.mainnet, ChainIds.optimism, ChainIds.arbitrum, ChainIds.linea, ChainIds.base];
362+
363+
if (!supportedChainIds.includes(chainId)) {
364+
for (const transactionHash of Object.keys(depositEventsByTxHash)) {
365+
wethEventsByTxHash[transactionHash] = [];
251366
}
367+
return wethEventsByTxHash;
252368
}
369+
370+
const eventName = this.getWethEventNameByChainId(chainId);
371+
const abi = this.getWethContractAbiByChainId(chainId);
372+
373+
for (const transactionHash of Object.keys(depositEventsByTxHash)) {
374+
const txReceipt = await this.providers.getProvider(chainId).getTransactionReceipt(transactionHash);
375+
const wethEvents = this.providers.parseTransactionReceiptLogs(txReceipt, eventName, abi) as any;
376+
wethEventsByTxHash[transactionHash] = wethEvents;
377+
}
378+
379+
return wethEventsByTxHash;
380+
}
381+
382+
private getWethContractAbiByChainId(chainId: number) {
383+
let abi = undefined;
384+
switch (chainId) {
385+
case ChainIds.mainnet:
386+
abi = WethMainnetAbi;
387+
break;
388+
case ChainIds.optimism:
389+
abi = WethOptimismAbi;
390+
break;
391+
case ChainIds.arbitrum:
392+
abi = WethArbitrumAbi;
393+
break;
394+
case ChainIds.linea:
395+
abi = WethLineaAbi;
396+
break;
397+
case ChainIds.base:
398+
abi = WethBaseAbi;
399+
break;
400+
default:
401+
throw new Error(`Unkown weth event name for chainId ${chainId}`);
402+
}
403+
return abi;
404+
}
405+
406+
private getWethEventNameByChainId(chainId: number) {
407+
let eventName = "";
408+
409+
switch (chainId) {
410+
case ChainIds.mainnet:
411+
eventName = "Deposit";
412+
break;
413+
case ChainIds.optimism:
414+
eventName = "Deposit";
415+
break;
416+
case ChainIds.arbitrum:
417+
eventName = "Transfer";
418+
break;
419+
case ChainIds.linea:
420+
eventName = "Deposit";
421+
break;
422+
case ChainIds.base:
423+
eventName = "Deposit";
424+
break;
425+
default:
426+
throw new Error(`Unkown weth event name for chainId ${chainId}`);
427+
}
428+
return eventName;
253429
}
254430

255431
private async processFillEvents(chainId: number, events: Event[]) {
@@ -400,6 +576,7 @@ export class BlocksEventsConsumer {
400576
chainId: number,
401577
depositEvent: FundsDepositedV3Event,
402578
swapEvent?: SwapBeforeBridgeEvent,
579+
wethEvent?: any,
403580
) {
404581
const { transactionHash, blockNumber } = depositEvent;
405582
const {
@@ -419,14 +596,14 @@ export class BlocksEventsConsumer {
419596
const wei = BigNumber.from(10).pow(18);
420597
const feePct = inputAmount.eq(0) ? BigNumber.from(0) : wei.sub(outputAmount.mul(wei).div(inputAmount));
421598
const txReceipt = await this.providers.getCachedTransactionReceipt(chainId, transactionHash);
422-
const swapToken = swapEvent ? await this.providers.getCachedToken(chainId, swapEvent.args.swapToken) : undefined;
423599
let trueDepositor = depositor;
424600
let exclusivityDeadlineDate = undefined;
425601

426602
if (exclusivityDeadline > 0) exclusivityDeadlineDate = new Date(exclusivityDeadline * 1000);
427603
if (depositor === SPOKE_POOL_VERIFIER_CONTRACT_ADDRESS) {
428604
trueDepositor = txReceipt.from;
429605
}
606+
const swapTokenValues = await this.extractSwapTokenValues(chainId, swapEvent, wethEvent);
430607

431608
return this.depositRepository.create({
432609
depositId,
@@ -452,12 +629,62 @@ export class BlocksEventsConsumer {
452629
relayer,
453630
message,
454631
// swap event properties
455-
swapTokenId: swapToken?.id,
456-
swapTokenAmount: swapEvent?.args.swapTokenAmount.toString(),
457-
swapTokenAddress: swapEvent?.args.swapToken,
632+
...swapTokenValues,
458633
});
459634
}
460635

636+
private async extractSwapTokenValues(chainId: number, swapEvent: SwapBeforeBridgeEvent, wethEvent: any) {
637+
let swapTokenValues = {
638+
swapTokenId: undefined,
639+
swapTokenAmount: undefined,
640+
swapTokenAddress: undefined,
641+
};
642+
643+
if (swapEvent) {
644+
const swapToken = await this.providers.getCachedToken(chainId, swapEvent.args.swapToken);
645+
swapTokenValues = {
646+
swapTokenId: swapToken.id,
647+
swapTokenAmount: swapEvent.args.swapTokenAmount.toString(),
648+
swapTokenAddress: swapEvent.args.swapToken,
649+
};
650+
} else if (wethEvent) {
651+
if (chainId === ChainIds.mainnet) {
652+
swapTokenValues = {
653+
swapTokenId: undefined,
654+
swapTokenAmount: (wethEvent as WethDepositEvent).args.wad.toString(),
655+
swapTokenAddress: "native",
656+
};
657+
} else if (chainId === ChainIds.optimism) {
658+
swapTokenValues = {
659+
swapTokenId: undefined,
660+
swapTokenAmount: (wethEvent as WethDepositEventOptimism).args.wad.toString(),
661+
swapTokenAddress: "native",
662+
};
663+
} else if (chainId === ChainIds.arbitrum) {
664+
swapTokenValues = {
665+
swapTokenId: undefined,
666+
swapTokenAmount: (wethEvent as WethTransfetEventArbitrum).args.value.toString(),
667+
swapTokenAddress: "native",
668+
};
669+
} else if (chainId === ChainIds.linea) {
670+
swapTokenValues = {
671+
swapTokenId: undefined,
672+
swapTokenAmount: (wethEvent as WethDepositEventLinea).args.wad.toString(),
673+
swapTokenAddress: "native",
674+
};
675+
} else if (chainId === ChainIds.base) {
676+
swapTokenValues = {
677+
swapTokenId: undefined,
678+
swapTokenAmount: (wethEvent as WethDepositEventBase).args.wad.toString(),
679+
swapTokenAddress: "native",
680+
};
681+
} else {
682+
throw new Error(`Unkown swap token values for chainId ${chainId}`);
683+
}
684+
}
685+
return swapTokenValues;
686+
}
687+
461688
// private async insertRawDepositEvent(chainId: number, event: Event) {
462689
// const typedEvent = event as FundsDepositedEvent2 | FundsDepositedEvent2_5;
463690
// const { blockNumber, blockHash, transactionIndex, address, transactionHash, logIndex, args } = typedEvent;

Diff for: src/modules/web3/model/weth-events.ts

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { BigNumber, Event } from "ethers";
2+
3+
export interface WethDepositEvent extends Event {
4+
args: [string, BigNumber] & {
5+
dst: string;
6+
wad: BigNumber;
7+
};
8+
}
9+
10+
export interface WethDepositEventOptimism extends Event {
11+
args: [string, BigNumber] & {
12+
dst: string;
13+
wad: BigNumber;
14+
};
15+
}
16+
17+
export interface WethDepositEventLinea extends Event {
18+
args: [string, BigNumber] & {
19+
dst: string;
20+
wad: BigNumber;
21+
};
22+
}
23+
24+
export interface WethDepositEventBase extends Event {
25+
args: [string, BigNumber] & {
26+
dst: string;
27+
wad: BigNumber;
28+
};
29+
}
30+
31+
export interface WethTransfetEventArbitrum extends Event {
32+
args: [string, string, BigNumber] & {
33+
from: string;
34+
to: string;
35+
value: BigNumber;
36+
};
37+
}

0 commit comments

Comments
 (0)