diff --git a/src/indexer/index.ts b/src/indexer/index.ts index 7beb004..3ef2a74 100644 --- a/src/indexer/index.ts +++ b/src/indexer/index.ts @@ -82,6 +82,21 @@ const contracts: { [contract in Contract]: ContractInfo } = { }, } +// Exported to allow for testing +export async function getBlockTimestamps(events: EventLog[], kit: ContractKit) { + const blockNumberToTimestamp: Record = {} + const uniqueBlockNumbers = new Set( + events.map(({ blockNumber }) => blockNumber), + ) + await Promise.all( + Array.from(uniqueBlockNumbers).map(async (blockNumber) => { + const block = await kit.web3.eth.getBlock(blockNumber) + blockNumberToTimestamp[blockNumber] = Number(block.timestamp) + }), + ) + return blockNumberToTimestamp +} + export async function indexEvents( contractKey: Contract, contractEvent: Event, @@ -111,6 +126,7 @@ export async function indexEvents( `${key} - Got ${events.length} events between blocks [${fromBlock}, ${toBlock}]`, ) } + const blockNumberToTimestamp = await getBlockTimestamps(events, kit) fromBlock = toBlock + 1 // Wrap write to event table and block index in a transaction so that the // update is atomic. @@ -123,6 +139,7 @@ export async function indexEvents( logIndex, blockNumber, blockHash, + blockTimestamp: blockNumberToTimestamp[blockNumber], ...payloadMapper(event), }) .transacting(trx) diff --git a/src/migrations/20230221212034_CreateTimestamp.ts b/src/migrations/20230221212034_CreateTimestamp.ts new file mode 100644 index 0000000..47bdbd4 --- /dev/null +++ b/src/migrations/20230221212034_CreateTimestamp.ts @@ -0,0 +1,30 @@ +import { Knex } from 'knex' + +const TABLE_NAMES = [ + 'account_wallet_mappings', + 'attestations_completed', + 'escrow', + 'transfers', +] + +export async function up(knex: Knex): Promise { + await Promise.all( + TABLE_NAMES.map( + async (tableName) => + await knex.schema.alterTable(tableName, (table) => + table.bigInteger('blockTimestamp'), + ), + ), + ) +} + +export async function down(knex: Knex): Promise { + await Promise.all( + TABLE_NAMES.map( + async (tableName) => + await knex.schema.alterTable(tableName, (table) => + table.dropColumn('blockTimestamp'), + ), + ), + ) +} diff --git a/test/indexer.test.ts b/test/indexer.test.ts index 9d9fff5..e1c4ad6 100644 --- a/test/indexer.test.ts +++ b/test/indexer.test.ts @@ -1,16 +1,19 @@ -import { Contract, Event, indexEvents } from '../src/indexer' +import { Contract, Event, indexEvents, getBlockTimestamps } from '../src/indexer' import { database, initDatabase } from '../src/database/db' import { partialEventLog } from '../src/util/testing' import { getLastBlock } from '../src/indexer/blocks' +import { getContractKit } from '../src/util/utils' const getLastBlockNumberMock = jest.fn() const getAccountEventsMock = jest.fn() +const getBlockMock = jest.fn() jest.mock('../src/util/utils', () => ({ getContractKit: jest.fn(() => ({ web3: { eth: { getBlockNumber: getLastBlockNumberMock, + getBlock: getBlockMock }, }, contracts: { @@ -47,6 +50,7 @@ describe('Indexer', () => { getAccountEventsMock .mockImplementationOnce(() => [partialEventLog({ transactionHash: firstTxHash })]) .mockImplementationOnce(() => [partialEventLog({ transactionHash: secondTxHash })]) + getBlockMock.mockImplementation((blockNumber) => ({timestamp: blockNumber})) } it('indexes account events', async () => { @@ -88,4 +92,23 @@ describe('Indexer', () => { const key = `${Contract.Accounts}_${Event.AccountWalletAddressSet}` expect(await getLastBlock(key)).toEqual(0) }) + + it('getBlockTimestamps returns a mapping from blockNumber to timestamp', async () => { + const blockNumber1 = 1234 + const blockNumber2 = 1235 + const kit = await getContractKit() + const blockNumberToTimestamp = await getBlockTimestamps([partialEventLog({blockNumber: blockNumber1}), partialEventLog({blockNumber: blockNumber2})], kit) + + expect(blockNumberToTimestamp).toEqual({[blockNumber1]: blockNumber1, [blockNumber2]: blockNumber2}) + expect(getBlockMock).toHaveBeenCalledTimes(2) + }) + + it('getBlockTimestamps calls getBlock once if multiple events are in one block', async () => { + const blockNumber = 1234 + const kit = await getContractKit() + const blockNumberToTimestamp = await getBlockTimestamps([partialEventLog({blockNumber}), partialEventLog({blockNumber})], kit) + + expect(blockNumberToTimestamp).toEqual({[blockNumber]: blockNumber}) + expect(getBlockMock).toHaveBeenCalledTimes(1) + }) })