Skip to content

Commit

Permalink
feat: add oapp remotes resolver layer
Browse files Browse the repository at this point in the history
refac: abstract away resolvers
refac: migrate memory height into abstract indexer
  • Loading branch information
sdlyy committed Mar 19, 2024
1 parent 1403053 commit 58e6805
Show file tree
Hide file tree
Showing 13 changed files with 331 additions and 163 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Logger } from '@l2beat/backend-tools'
import { ChainId } from '@lz/libs'
import { expect } from 'earl'

import { setupDatabaseTestSuite } from '../../test/database'
import { OAppRemoteRecord, OAppRemoteRepository } from './OAppRemoteRepository'

describe(OAppRemoteRepository.name, () => {
const { database } = setupDatabaseTestSuite()
const repository = new OAppRemoteRepository(database, Logger.SILENT)

before(async () => await repository.deleteAll())
afterEach(async () => await repository.deleteAll())

describe(OAppRemoteRepository.prototype.addMany.name, () => {
it('merges rows on insert', async () => {
const record1 = mockRecord({ oAppId: 1, targetChainId: ChainId.ETHEREUM })
const record2 = mockRecord({ oAppId: 2, targetChainId: ChainId.OPTIMISM })

await repository.addMany([record1, record2])

const recordsBeforeMerge = await repository.findAll()

await repository.addMany([record1, record2])

const recordsAfterMerge = await repository.findAll()

expect(recordsBeforeMerge.length).toEqual(2)
expect(recordsAfterMerge.length).toEqual(2)
})
})
})

function mockRecord(overrides?: Partial<OAppRemoteRecord>): OAppRemoteRecord {
return {
oAppId: 1,
targetChainId: ChainId.ETHEREUM,
...overrides,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,6 @@ export class OAppRemoteRepository extends BaseRepository {

return rows.map(toRecord)
}
public async findByOAppIds(oAppIds: number[]): Promise<OAppRemoteRecord[]> {
const knex = await this.knex()

const rows = await knex('oapp_remote')
.select('*')
.whereIn('oapp_id', oAppIds)

return rows.map(toRecord)
}

async deleteAll(): Promise<number> {
const knex = await this.knex()
Expand Down
10 changes: 10 additions & 0 deletions packages/backend/src/tracking/TrackingModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import { OAppConfigurationIndexer } from './domain/indexers/OAppConfigurationInd
import { OAppListIndexer } from './domain/indexers/OAppListIndexer'
import { OAppRemoteIndexer } from './domain/indexers/OAppRemotesIndexer'
import { DiscoveryDefaultConfigurationsProvider } from './domain/providers/DefaultConfigurationsProvider'
import { OFTInterfaceResolver } from './domain/providers/interface-resolvers/OFTInterfaceResolver'
import { StargateInterfaceResolver } from './domain/providers/interface-resolvers/StargateResolver'
import { BlockchainOAppConfigurationProvider } from './domain/providers/OAppConfigurationProvider'
import { BlockchainOAppRemotesProvider } from './domain/providers/OAppRemotesProvider'
import { HttpOAppListProvider } from './domain/providers/OAppsListProvider'
Expand Down Expand Up @@ -144,6 +146,9 @@ function createTrackingSubmodule(

const httpClient = new HttpClient()

const OFTResolver = new OFTInterfaceResolver(multicall)
const stargateResolver = new StargateInterfaceResolver(multicall)

const oAppListProvider = new HttpOAppListProvider(
logger,
httpClient,
Expand All @@ -165,10 +170,15 @@ function createTrackingSubmodule(
logger,
)

const supportedChains = ChainId.getAll()
const resolvers = [OFTResolver, stargateResolver]

const oAppRemotesProvider = new BlockchainOAppRemotesProvider(
provider,
multicall,
chainId,
supportedChains,
resolvers,
logger,
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Logger } from '@l2beat/backend-tools'
import { ChildIndexer, Indexer } from '@l2beat/uif'
import { Indexer } from '@l2beat/uif'
import { ChainId } from '@lz/libs'

import {
Expand All @@ -9,9 +9,9 @@ import {
import { OAppConfigurations } from '../configuration'
import { ProtocolVersion } from '../const'
import { DefaultConfigurationsProvider } from '../providers/DefaultConfigurationsProvider'
import { InMemoryIndexer } from './InMemoryIndexer'

export class DefaultConfigurationIndexer extends ChildIndexer {
protected height = 0
export class DefaultConfigurationIndexer extends InMemoryIndexer {
constructor(
logger: Logger,
private readonly chainId: ChainId,
Expand Down Expand Up @@ -40,19 +40,6 @@ export class DefaultConfigurationIndexer extends ChildIndexer {

return to
}

public override getSafeHeight(): Promise<number> {
return Promise.resolve(this.height)
}

protected override setSafeHeight(height: number): Promise<void> {
this.height = height
return Promise.resolve()
}

protected override invalidate(targetHeight: number): Promise<number> {
return Promise.resolve(targetHeight)
}
}

function configToRecords(
Expand Down
19 changes: 19 additions & 0 deletions packages/backend/src/tracking/domain/indexers/InMemoryIndexer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ChildIndexer } from '@l2beat/uif'

export { InMemoryIndexer }

abstract class InMemoryIndexer extends ChildIndexer {
protected height = 0
public override getSafeHeight(): Promise<number> {
return Promise.resolve(this.height)
}

protected override setSafeHeight(height: number): Promise<void> {
this.height = height
return Promise.resolve()
}

protected override invalidate(targetHeight: number): Promise<number> {
return Promise.resolve(targetHeight)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Logger } from '@l2beat/backend-tools'
import { ChildIndexer, Indexer } from '@l2beat/uif'
import { Indexer } from '@l2beat/uif'
import { ChainId } from '@lz/libs'

import {
Expand All @@ -10,9 +10,9 @@ import { OAppRemoteRepository } from '../../../peripherals/database/OAppRemoteRe
import { OAppRepository } from '../../../peripherals/database/OAppRepository'
import { OAppConfigurations } from '../configuration'
import { OAppConfigurationProvider } from '../providers/OAppConfigurationProvider'
import { InMemoryIndexer } from './InMemoryIndexer'

export class OAppConfigurationIndexer extends ChildIndexer {
protected height = 0
export class OAppConfigurationIndexer extends InMemoryIndexer {
constructor(
logger: Logger,
private readonly chainId: ChainId,
Expand All @@ -26,8 +26,10 @@ export class OAppConfigurationIndexer extends ChildIndexer {
}

protected override async update(_from: number, to: number): Promise<number> {
const oApps = await this.oAppRepo.getBySourceChain(this.chainId)
const oAppsRemotes = await this.oAppRemoteRepo.findAll()
const [oApps, oAppsRemotes] = await Promise.all([
this.oAppRepo.getBySourceChain(this.chainId),
this.oAppRemoteRepo.findAll(),
])

const configurationRecords = await Promise.all(
oApps.map(async (oApp) => {
Expand All @@ -49,19 +51,6 @@ export class OAppConfigurationIndexer extends ChildIndexer {

return to
}

public override getSafeHeight(): Promise<number> {
return Promise.resolve(this.height)
}

protected override setSafeHeight(height: number): Promise<void> {
this.height = height
return Promise.resolve()
}

protected override invalidate(targetHeight: number): Promise<number> {
return Promise.resolve(targetHeight)
}
}

function configToRecord(
Expand Down
19 changes: 3 additions & 16 deletions packages/backend/src/tracking/domain/indexers/OAppListIndexer.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Logger } from '@l2beat/backend-tools'
import { ChildIndexer, Indexer } from '@l2beat/uif'
import { Indexer } from '@l2beat/uif'
import { ChainId } from '@lz/libs'

import { OAppRepository } from '../../../peripherals/database/OAppRepository'
import { ProtocolVersion } from '../const'
import { OAppListProvider } from '../providers/OAppsListProvider'
import { InMemoryIndexer } from './InMemoryIndexer'

export class OAppListIndexer extends ChildIndexer {
protected height = 0
export class OAppListIndexer extends InMemoryIndexer {
constructor(
logger: Logger,
private readonly chainId: ChainId,
Expand Down Expand Up @@ -38,17 +38,4 @@ export class OAppListIndexer extends ChildIndexer {

return to
}

public override getSafeHeight(): Promise<number> {
return Promise.resolve(this.height)
}

protected override setSafeHeight(height: number): Promise<void> {
this.height = height
return Promise.resolve()
}

protected override invalidate(targetHeight: number): Promise<number> {
return Promise.resolve(targetHeight)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Logger } from '@l2beat/backend-tools'
import { ChildIndexer, Indexer } from '@l2beat/uif'
import { Indexer } from '@l2beat/uif'
import { ChainId } from '@lz/libs'

import {
Expand All @@ -8,9 +8,9 @@ import {
} from '../../../peripherals/database/OAppRemoteRepository'
import { OAppRepository } from '../../../peripherals/database/OAppRepository'
import { OAppRemotesProvider } from '../providers/OAppRemotesProvider'
import { InMemoryIndexer } from './InMemoryIndexer'

export class OAppRemoteIndexer extends ChildIndexer {
protected height = 0
export class OAppRemoteIndexer extends InMemoryIndexer {
constructor(
logger: Logger,
private readonly chainId: ChainId,
Expand Down Expand Up @@ -42,17 +42,4 @@ export class OAppRemoteIndexer extends ChildIndexer {

return to
}

public override getSafeHeight(): Promise<number> {
return Promise.resolve(this.height)
}

protected override setSafeHeight(height: number): Promise<void> {
this.height = height
return Promise.resolve()
}

protected override invalidate(targetHeight: number): Promise<number> {
return Promise.resolve(targetHeight)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Logger } from '@l2beat/backend-tools'
import { MulticallClient } from '@l2beat/discovery'
// eslint-disable-next-line import/no-internal-modules
import { MulticallResponse } from '@l2beat/discovery/dist/discovery/provider/multicall/types'
// eslint-disable-next-line import/no-internal-modules
import { Bytes } from '@l2beat/discovery/dist/utils/Bytes'
import { ChainId, EthereumAddress } from '@lz/libs'
import { expect, mockFn, mockObject } from 'earl'
import { providers } from 'ethers'

import { OAppInterfaceResolver } from './interface-resolvers/resolver'
import { BlockchainOAppRemotesProvider } from './OAppRemotesProvider'

describe(BlockchainOAppRemotesProvider.name, () => {
it('resolves available remotes for given OApp', async () => {
const blockNumber = 123
const oAppAddress = EthereumAddress.random()
const rpcProvider = mockObject<providers.StaticJsonRpcProvider>({
getBlockNumber: mockFn().resolvesTo(blockNumber),
})

const mcResponse: MulticallResponse[] = ChainId.getAll().map(() => ({
success: true,
data: Bytes.fromHex('0x0'),
}))

const multicall = mockObject<MulticallClient>({
multicall: mockFn().resolvesTo(mcResponse),
})

const supportedChains = [ChainId.ETHEREUM, ChainId.ARBITRUM]

const resolverA: OAppInterfaceResolver = {
isSupported: async () => false,
encode: () => ({ address: oAppAddress, data: Bytes.fromHex('0x0') }),
decode: () => true,
}

const resolverB: OAppInterfaceResolver = {
isSupported: async () => true,
encode: () => ({ address: oAppAddress, data: Bytes.fromHex('0x0') }),
decode: () => true,
}

const provider = new BlockchainOAppRemotesProvider(
rpcProvider,
multicall,
ChainId.ETHEREUM,
supportedChains,
[resolverA, resolverB],
Logger.SILENT,
)

const result = await provider.getSupportedRemotes(oAppAddress)

expect(result).toEqual(supportedChains)
})
})
Loading

0 comments on commit 58e6805

Please sign in to comment.