Skip to content

Commit

Permalink
feat(api): periodically update API feeds from v2 contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
Tommytrg committed Jun 20, 2024
1 parent 82c2dda commit 663b27b
Show file tree
Hide file tree
Showing 13 changed files with 1,175 additions and 115 deletions.
151 changes: 98 additions & 53 deletions packages/api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,76 +13,121 @@ import {
} from '../types'
import { Web3Middleware } from './web3Middleware/index'
import { normalizeNetworkConfig } from './utils/index'
import {
normalizeAndValidateDataFeedConfig,
fetchDataFeedsRouterConfig,
} from './readDataFeeds'
import { fetchFeedsLegacy, fetchDataFeedsRouterConfig } from './readDataFeeds'
import { SvgCache } from './svgCache'
import { NetworkRouter } from './web3Middleware/NetworkRouter'
import { Configuration } from './web3Middleware/Configuration'
import { FeedsState } from './repository/feedState'

class DataFeedsExplorer {
routers: Array<NetworkRouter>
repositories: Repositories
configurationFile: RouterDataFeedsConfig
configuration: Configuration
networksConfig

svgCache: SvgCache
mongoManager: MongoManager
feedsState: FeedsState

constructor() {
this.svgCache = new SvgCache()
this.mongoManager = new MongoManager()
this.feedsState = new FeedsState()
}

async initializeNetworkRouters() {
return this.configuration
.listNetworksUsingPriceFeedsContract()
.filter((config) => {
if (!config.provider) {
console.warn('No provider found for ', config.key)
}
return config.provider
})
.map(
(networkInfo) =>
new NetworkRouter(
this.configuration,
Web3,
this.repositories,
networkInfo,
),
)
}

async initializeConfiguration(svgCache: SvgCache) {
this.configurationFile = await fetchDataFeedsRouterConfig()
this.configuration = new Configuration(this.configurationFile)
this.networksConfig = await fetchNetworkConfigurations(
this.configurationFile,
svgCache,
)
}

async start() {
const db = await this.mongoManager.start()

async function main() {
const svgCache = new SvgCache()
const mongoManager = new MongoManager()
const db = await mongoManager.start()
const configurationFile: RouterDataFeedsConfig =
await fetchDataFeedsRouterConfig()
const configuration = new Configuration(configurationFile)
await this.initializeConfiguration(this.svgCache)
this.routers = await this.initializeNetworkRouters()

const legacyFeeds: Array<FeedInfo> = fetchFeedsLegacy(
this.configurationFile,
)
const v2Feeds: Array<FeedInfo> = await fetchFeedsV2(this.routers)

this.repositories = {
feedRepository: new FeedRepository(this.feedsState),
resultRequestRepository: new ResultRequestRepository(db),
}
console.log('legacyFeeds', JSON.stringify(v2Feeds))
this.repositories.feedRepository.setLegacyFeeds(legacyFeeds)
this.repositories.feedRepository.setV2Feeds(v2Feeds)

const web3Middleware = new Web3Middleware(
this.configuration,
{ repositories: this.repositories, Web3: Web3 },
legacyFeeds,
)

web3Middleware.listen()

await createServer(this.repositories, this.svgCache, {
networksConfig: this.networksConfig,
configuration: this.configuration,
})
}
}

const legacyFeeds: Array<FeedInfo> =
normalizeAndValidateDataFeedConfig(configurationFile)
async function fetchFeedsV2(
routers: Array<NetworkRouter>,
): Promise<Array<FeedInfo>> {
const promises = routers.map(async (router) => await router.getFeedInfos())
const newFeeds: Array<FeedInfo> = (await Promise.all(promises)).flat()

return newFeeds
}

async function fetchNetworkConfigurations(
configurationFile,
svgCache,
): Promise<Array<NetworksConfig>> {
const networksConfigPartial: Array<Omit<NetworksConfig, 'logo'>> =
normalizeNetworkConfig(configurationFile)

const logosToFetch = networksConfigPartial.map(
(networksConfig: NetworksConfig) => {
return networksConfig.chain.toLowerCase()
},
)

const networksLogos: { [key: string]: string } =
await svgCache.getMany(logosToFetch)

const networksConfig = networksConfigPartial.map((networksConfig, index) => ({
return networksConfigPartial.map((networksConfig, index) => ({
...networksConfig,
logo: networksLogos[logosToFetch[index]],
}))

const routers = configuration
.listNetworksUsingPriceFeedsContract()
.filter((config) => {
if (!config.provider) {
console.warn('No provider found for ', config.key)
}
return config.provider
})
.map(
(networkInfo) =>
new NetworkRouter(configuration, Web3, repositories, networkInfo),
)

const promises = routers.map(async (router) => await router.getFeedInfos())

const newFeeds: Array<FeedInfo> = (await Promise.all(promises)).flat()
const feeds = [...legacyFeeds, ...newFeeds]
const repositories: Repositories = {
feedRepository: new FeedRepository(feeds),
resultRequestRepository: new ResultRequestRepository(db, feeds),
}

const web3Middleware = new Web3Middleware(
configuration,
{ repositories, Web3: Web3 },
legacyFeeds,
)

web3Middleware.listen()

await createServer(repositories, svgCache, {
dataFeedsConfig: feeds,
networksConfig,
configuration,
})
}

main()
const dfe = new DataFeedsExplorer()

dfe.start()
4 changes: 2 additions & 2 deletions packages/api/src/readDataFeeds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ export async function fetchDataFeedsRouterConfig(): Promise<RouterDataFeedsConfi
})
}
/**
* FIXME(#197): normalizeAndValidateDataFeedConfig could be refactored to include the ABI to avoid
* FIXME(#197): fetchFeedsLegacy could be refactored to include the ABI to avoid
* have multiple functions to build the object. So we should review how we are fetching,
* validating and normalizing the configuration file We can even review the normalized object
* structure to check if it has sense right now or only because it was the previous configuration
* format
*/
export function normalizeAndValidateDataFeedConfig(
export function fetchFeedsLegacy(
config: RouterDataFeedsConfig,
): Array<FeedInfo> {
const dataFeeds: Array<Omit<FeedInfoConfig, 'abi' | 'routerAbi'>> =
Expand Down
81 changes: 60 additions & 21 deletions packages/api/src/repository/Feed.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import { PaginatedFeedsObject, FeedInfo } from '../../types'
import { FeedsState } from './feedState'
import { PaginatedFeedsObject, FeedInfo, ConfigByFullName } from '../../types'

export class FeedRepository {
dataFeeds: Array<FeedInfo>
feedsState: FeedsState
// TODO: replace string with Network
dataFeedsByNetwork: Record<string, Array<FeedInfo>>
configByFullName: ConfigByFullName

constructor(dataFeeds: Array<FeedInfo>) {
this.dataFeedsByNetwork = dataFeeds.reduce(
constructor(feedState: FeedsState) {
this.feedsState = feedState
this.initialize()
}

initialize() {
const feeds = this.feedsState.listFeeds()

this.dataFeedsByNetwork = feeds.reduce(
(acc: Record<string, Array<FeedInfo>>, feedInfo: FeedInfo) => ({
...acc,
[feedInfo.network]: acc[feedInfo.network]
Expand All @@ -15,11 +24,23 @@ export class FeedRepository {
}),
{},
)
this.dataFeeds = dataFeeds
this.configByFullName = feeds.reduce(
(acc, feedInfo) => ({
...acc,
[`${feedInfo.feedFullName}`]: feedInfo,
}),
{},
)
}

getConfigByFullName() {
return this.configByFullName
}

get(feedFullName: string): FeedInfo {
return this.dataFeeds.find((feed) => feed.feedFullName === feedFullName)
return this.feedsState
.listFeeds()
.find((feed) => feed.feedFullName === feedFullName)
}

async getFeedsByNetwork(
Expand All @@ -45,20 +66,38 @@ export class FeedRepository {
const hasSameFeedFullName = (feed: FeedInfo) =>
feed.feedFullName === feedFullName

// Update address in sortedDataFeeds
const sortedDataFeedIndex = this.dataFeeds.findIndex(hasSameFeedFullName)
const feed = this.dataFeeds[sortedDataFeedIndex]
feed.address = address
feed.contractId = contractId
// Update address in dataFeedsByNetwork cache
const dataFeedsByNetworkIndex =
this.dataFeedsByNetwork[feed.network].findIndex(hasSameFeedFullName)
this.dataFeedsByNetwork[feed.network][dataFeedsByNetworkIndex].address =
address
// Update contractId in dataFeedsByNetwork cache
this.dataFeedsByNetwork[feed.network][dataFeedsByNetworkIndex].contractId =
contractId

return feed
const legacyFeeds = this.feedsState.getLegacyFeeds()
const index = legacyFeeds.findIndex(hasSameFeedFullName)
const updatedFeed = { ...legacyFeeds[index], address, contractId }
legacyFeeds[index] = updatedFeed
this.feedsState.setLegacyFeeds(legacyFeeds)

this.initialize()

return updatedFeed
}

refreshV2NetworkFeeds(network: string, feedInfos: Array<FeedInfo>) {
const v2Feeds = this.feedsState.getV2Feeds()

const newV2Feeds = v2Feeds
.filter((feed) => {
feed.network !== network
})
.concat(feedInfos)

this.feedsState.setV2Feeds(newV2Feeds)

this.initialize()
}

setLegacyFeeds(legacyFeeds: Array<FeedInfo>) {
this.feedsState.setLegacyFeeds(legacyFeeds)
this.initialize()
}

setV2Feeds(v2Feeds: Array<FeedInfo>) {
this.feedsState.setV2Feeds(v2Feeds)
this.initialize()
}
}
3 changes: 1 addition & 2 deletions packages/api/src/repository/ResultRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
ResultRequestDbObject,
Db,
Collection,
FeedInfo,
WithoutId,
} from '../../types'
import { containFalsyValues } from './containFalsyValues'
Expand All @@ -15,7 +14,7 @@ export class ResultRequestRepository {
>
latestResults: Record<string, WithoutId<ResultRequestDbObject>> = {}

constructor(db: Db, _dataFeeds: Array<FeedInfo>) {
constructor(db: Db) {
this.collection = db.collection('result_request')
}

Expand Down
31 changes: 31 additions & 0 deletions packages/api/src/repository/feedState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { FeedInfo } from '../../types'

export class FeedsState {
private legacyFeeds: Array<FeedInfo>
private v2Feeds: Array<FeedInfo>

constructor() {
this.legacyFeeds = []
this.v2Feeds = []
}

setV2Feeds(v2Feeds: Array<FeedInfo>) {
this.v2Feeds = v2Feeds
}

setLegacyFeeds(legacyFeeds: Array<FeedInfo>) {
this.legacyFeeds = legacyFeeds
}

getV2Feeds(): Array<FeedInfo> {
return this.v2Feeds
}

getLegacyFeeds(): Array<FeedInfo> {
return this.legacyFeeds
}

listFeeds(): Array<FeedInfo> {
return [...this.legacyFeeds, ...this.v2Feeds]
}
}
Loading

0 comments on commit 663b27b

Please sign in to comment.