Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/indexer/src/data-indexing/model/abis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,11 @@ export const CCTP_DEPOSIT_FOR_BURN_ABI = [
];

export const MESSAGE_SENT_ABI = ["event MessageSent(bytes message)"];

/* ==================================================================================
* OFT DOMAIN LOGIC & CONFIGURATION
* * Specific ABIs for the Omni-chain Fungible Token (OFT) protocol.
* ================================================================================== */
export const OFT_SENT_ABI = [
"event OFTSent(bytes32 indexed guid, uint32 dstEid, address indexed fromAddress, uint256 amountSentLD, uint256 amountReceivedLD)",
];
11 changes: 11 additions & 0 deletions packages/indexer/src/data-indexing/model/eventTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,14 @@ export interface DepositForBurnArgs {
export interface MessageSentArgs {
message: `0x${string}`;
}
/* ==================================================================================
* OFT DOMAIN LOGIC & CONFIGURATION
* * Specific event types for the Omni-chain Fungible Token (OFT) protocol.
* ================================================================================== */
export interface OftSentArgs {
guid: `0x${string}`;
fromAddress: `0x${string}`;
dstEid: number;
amountSentLD: number;
amountReceivedLD: number;
}
6 changes: 6 additions & 0 deletions packages/indexer/src/data-indexing/service/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,9 @@ export const WHITELISTED_FINALIZERS: Array<`0x${string}`> = [
];
export const DEPOSIT_FOR_BURN_EVENT_NAME = "DepositForBurn";
export const MESSAGE_SENT_EVENT_NAME = "MessageSent";

/* ==================================================================================
* OFT DOMAIN LOGIC & CONFIGURATION
* * Specific implementations for the Omni-chain Fungible Token (OFT) protocol.
* ================================================================================== */
export const OFTSENT_EVENT_NAME = "OFTSent";
62 changes: 56 additions & 6 deletions packages/indexer/src/data-indexing/service/indexing.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,34 @@
import { IndexerConfig, startIndexing } from "./genericIndexing";
import { CHAIN_IDs, MAINNET_CHAIN_IDs } from "@across-protocol/constants";
import { CHAIN_IDs } from "@across-protocol/constants";
import { IndexerEventPayload } from "./genericEventListening";
import { Entity } from "typeorm";
import {
TOKEN_MESSENGER_ADDRESS_MAINNET,
DEPOSIT_FOR_BURN_EVENT_NAME,
MESSAGE_SENT_EVENT_NAME,
OFTSENT_EVENT_NAME,
MESSAGE_TRANSMITTER_ADDRESS_MAINNET,
TOKEN_MESSENGER_ADDRESS_TESTNET,
MESSAGE_TRANSMITTER_ADDRESS_TESTNET,
} from "./constants";
import { CCTP_DEPOSIT_FOR_BURN_ABI, MESSAGE_SENT_ABI } from "../model/abis";
import {
CCTP_DEPOSIT_FOR_BURN_ABI,
MESSAGE_SENT_ABI,
OFT_SENT_ABI,
} from "../model/abis";
import {
storeDepositForBurnEvent,
storeMessageSentEvent,
storeOftSentEvent,
} from "./storing";
import {
transformDepositForBurnEvent,
transformMessageSentEvent,
transformOftSentEvent,
} from "./tranforming";
import { storeDepositForBurnEvent, storeMessageSentEvent } from "./storing";
import { utils as dbUtils } from "@repo/indexer-database";
import { Logger } from "winston";
import { getOftChainConfiguration } from "../adapter/oft/service";

/**
* Definition of the request object for starting an indexer.
Expand All @@ -32,7 +43,7 @@ export interface StartIndexerRequest {
}

/**
* Sets up and starts the indexer for events on Arbitrum Mainnet.
* Sets up and starts the indexer for events on Arbitrum.
*
* This function demonstrates how the generic components are assembled into a concrete
* indexer. To support a new event, one would need to add another event to the events array with its
Expand All @@ -43,7 +54,7 @@ export async function startArbitrumIndexing(request: StartIndexerRequest) {
// Destructure the request object
const { repo, rpcUrl, logger, sigterm } = request;
// Concrete Configuration
// Define the specific parameters for the Arbitrum Mainnet indexer.
// Define the specific parameters for the Arbitrum indexer.
const indexerConfig: IndexerConfig<
Partial<typeof Entity>,
dbUtils.BlockchainEventRepository,
Expand Down Expand Up @@ -81,7 +92,46 @@ export async function startArbitrumIndexing(request: StartIndexerRequest) {
// Start the generic indexer subsystem with our concrete configuration and functions.
await startIndexing({
db: repo,
indexerConfig,
indexerConfig: indexerConfig,
logger,
sigterm,
});
}

/**
* Sets up and starts the indexer for OFT events on hyperEVM.
* @param request The configuration object containing repo, rpcUrl, logger, and shutdown signal.
*/
export async function startHyperEvmIndexing(request: StartIndexerRequest) {
const { repo, rpcUrl, logger, sigterm, testNet } = request;
const chainId = testNet ? CHAIN_IDs.HYPEREVM_TESTNET : CHAIN_IDs.HYPEREVM;
const oftChainConfig = getOftChainConfiguration(chainId);
if (!oftChainConfig) {
throw new Error(`OFT configuration not found for chainId: ${chainId}`);
}

const indexerConfig: IndexerConfig<
Partial<typeof Entity>,
dbUtils.BlockchainEventRepository,
IndexerEventPayload
> = {
chainId,
rpcUrl,
events: oftChainConfig.tokens.map((token) => ({
config: {
address: token.address as `0x${string}`,
abi: OFT_SENT_ABI,
eventName: OFTSENT_EVENT_NAME,
fromBlock: token.startBlockNumber,
},
transform: transformOftSentEvent,
store: storeOftSentEvent,
})),
};

await startIndexing({
db: repo,
indexerConfig: indexerConfig,
logger,
sigterm,
});
Expand Down
17 changes: 17 additions & 0 deletions packages/indexer/src/data-indexing/service/storing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const PK_CHAIN_BLOCK_TX_LOG = [
"logIndex",
];

const PK_CHAIN_BLOCK_HASH_LOG = ["chainId", "blockHash", "logIndex"];

/**
* Stores a DepositForBurn event in the database.
*
Expand Down Expand Up @@ -45,3 +47,18 @@ export const storeMessageSentEvent: Storer<
[],
);
};

export const storeOftSentEvent: Storer<
Partial<entities.OFTSent>,
dbUtils.BlockchainEventRepository
> = async (
event: Partial<entities.OFTSent>,
repository: dbUtils.BlockchainEventRepository,
) => {
return repository.saveAndHandleFinalisationBatch<entities.OFTSent>(
entities.OFTSent,
[event],
PK_CHAIN_BLOCK_HASH_LOG as (keyof entities.OFTSent)[],
[],
);
};
28 changes: 27 additions & 1 deletion packages/indexer/src/data-indexing/service/tranforming.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ import {
import { formatFromAddressToChainFormat } from "../../utils";
import { Transformer } from "../model/genericTypes";
import { getFinalisedBlockBufferDistance } from "./constants";
import { DepositForBurnArgs, MessageSentArgs } from "../model/eventTypes";
import {
DepositForBurnArgs,
MessageSentArgs,
OftSentArgs,
} from "../model/eventTypes";
import { Logger } from "winston";
import { arrayify } from "ethers/lib/utils"; // New import
import { getOftChainConfiguration } from "../adapter/oft/service";

/**
* A generic transformer for addresses.
Expand Down Expand Up @@ -157,6 +162,27 @@ export const transformMessageSentEvent: Transformer<
};
};

export const transformOftSentEvent: Transformer<
IndexerEventPayload,
Partial<entities.OFTSent>
> = (payload, logger: Logger = console as unknown as Logger) => {
const rawArgs = getRawArgs(payload, logger);
const args = rawArgs as unknown as OftSentArgs;
const base = baseTransformer(payload, logger);
const chainId = parseInt(base.chainId);
const fromAddress = transformAddress(args.fromAddress, chainId);

return {
...base,
guid: args.guid,
dstEid: args.dstEid,
fromAddress,
amountSentLD: args.amountSentLD.toString(),
amountReceivedLD: args.amountReceivedLD.toString(),
token: getOftChainConfiguration(payload.chainId).tokens[0]!.address,
};
};

const getRawArgs = <TEvent>(payload: IndexerEventPayload, logger: Logger) => {
const rawArgs = (payload.log as any).args;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { expect } from "chai";
import { DataSource } from "typeorm";
import { getTestDataSource } from "../../tests/setup";
import { startArbitrumIndexing } from "../service/indexing";
import {
startArbitrumIndexing,
startHyperEvmIndexing,
} from "../service/indexing";
import { MockWebSocketRPCServer } from "../../tests/testProvider";
import { utils as dbUtils } from "@repo/indexer-database";
import { entities } from "@repo/indexer-database";
Expand All @@ -12,6 +15,7 @@ import {
import sinon from "sinon";
import { Logger } from "winston";
import { CHAIN_IDs } from "@across-protocol/constants";
import { getOftChainConfiguration } from "../adapter/oft/service";

describe("Indexer Integration (Real Transaction Data)", () => {
let dataSource: DataSource;
Expand Down Expand Up @@ -302,4 +306,78 @@ describe("Indexer Integration (Real Transaction Data)", () => {
// Null checks
expect(savedEvent!.deletedAt).to.be.null;
}).timeout(20000);

it("should ingest the OFTSent event from hyperEVM tx 0x94d7...cd9", async () => {
// Real transaction taken from:
// https://hyperevmscan.io/tx/0x94d7feace76e29767cbdb1c1ff83430a846e933040de9caaf167290d22315cd9#eventlog#18
const txHash =
"0x94d7feace76e29767cbdb1c1ff83430a846e933040de9caaf167290d22315cd9";
const blockNumber = 20670509;
const blockHash =
"0xa2ae33b41e3f1d1896ab84e625da2416f0dc9ee1fcd9c91b975eb118331364e9";
const blockTimestamp = "0x6564b1f3"; // An arbitrary timestamp

server.mockBlockResponse({
number: "0x" + blockNumber.toString(16),
hash: blockHash,
timestamp: blockTimestamp,
transactions: [],
});

startHyperEvmIndexing({
repo: blockchainRepository,
rpcUrl,
logger,
sigterm: abortController.signal,
});
await server.waitForSubscription();

const oftChainConfig = getOftChainConfiguration(CHAIN_IDs.HYPEREVM);
const token = oftChainConfig.tokens.find(
(t) => t.address === "0x904861a24F30EC96ea7CFC3bE9EA4B476d237e98",
)!;

server.pushEvent({
address: token.address,
blockNumber: "0x" + blockNumber.toString(16),
transactionHash: txHash,
logIndex: "0x12", // 18
blockHash,
transactionIndex: "0x1",
topics: [
"0x85496b760a4b7f8d66384b9df21b381f5d1b1e79f229a47aaf4c232edc2fe59a",
"0xfd74b08cc4da0b6cea03a2731ce3bf9c5fa6912cc6241557c39ce2f2dd20b002",
"0x00000000000000000000000034f9c0b11e67d72ad65c41ff90a6989846f28c22",
],
data:
"0x" +
"0000000000000000000000000000000000000000000000000000000000007670" + // dstEid: 30320
"0000000000000000000000000000000000000000000000000000000005f5e100" + // amountSentLD: 100000000
"0000000000000000000000000000000000000000000000000000000005f5e100", // amountReceivedLD: 100000000
});

await new Promise((r) => setTimeout(r, 500));

const oftSentRepo = dataSource.getRepository(entities.OFTSent);
const savedEvent = await oftSentRepo.findOne({
where: { transactionHash: txHash, logIndex: 18 },
});

expect(savedEvent).to.exist;

expect(savedEvent).to.deep.include({
chainId: CHAIN_IDs.HYPEREVM,
blockNumber: blockNumber,
blockHash,
transactionHash: txHash,
logIndex: 18,
finalised: false,
guid: "0xfd74b08cc4da0b6cea03a2731ce3bf9c5fa6912cc6241557c39ce2f2dd20b002",
dstEid: 30320,
fromAddress: "0x34F9C0B11e67d72AD65C41Ff90A6989846f28c22",
amountSentLD: 100000000,
amountReceivedLD: 100000000,
token: token.address,
});
}).timeout(20000);
});