Skip to content
This repository has been archived by the owner on Mar 4, 2024. It is now read-only.

Commit

Permalink
feat: add timestamp to indexed events (#42)
Browse files Browse the repository at this point in the history
Currently, the indexer does not store the timestamp associated with blockchain events. As part of [ACT-612](https://linear.app/valora/issue/ACT-612/add-time-field-to-valora-transfer-events), this PR adds this functionality and updates the database schema as required.
  • Loading branch information
anthonydaoud authored Feb 27, 2023
1 parent df46ff5 commit dab32b3
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 1 deletion.
17 changes: 17 additions & 0 deletions src/indexer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<number, number> = {}
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,
Expand Down Expand Up @@ -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.
Expand All @@ -123,6 +139,7 @@ export async function indexEvents(
logIndex,
blockNumber,
blockHash,
blockTimestamp: blockNumberToTimestamp[blockNumber],
...payloadMapper(event),
})
.transacting(trx)
Expand Down
30 changes: 30 additions & 0 deletions src/migrations/20230221212034_CreateTimestamp.ts
Original file line number Diff line number Diff line change
@@ -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<void> {
await Promise.all(
TABLE_NAMES.map(
async (tableName) =>
await knex.schema.alterTable(tableName, (table) =>
table.bigInteger('blockTimestamp'),
),
),
)
}

export async function down(knex: Knex): Promise<void> {
await Promise.all(
TABLE_NAMES.map(
async (tableName) =>
await knex.schema.alterTable(tableName, (table) =>
table.dropColumn('blockTimestamp'),
),
),
)
}
25 changes: 24 additions & 1 deletion test/indexer.test.ts
Original file line number Diff line number Diff line change
@@ -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: {
Expand Down Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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)
})
})

0 comments on commit dab32b3

Please sign in to comment.