Skip to content

Commit

Permalink
feat(api): add support for witnetPriceFeeds contract
Browse files Browse the repository at this point in the history
  • Loading branch information
Tommytrg committed Feb 9, 2024
1 parent 247d58a commit 011d4a9
Show file tree
Hide file tree
Showing 18 changed files with 13,370 additions and 640 deletions.
1,300 changes: 1,300 additions & 0 deletions packages/api/src/abi/WitnetPriceFeeds.json

Large diffs are not rendered by default.

1,163 changes: 580 additions & 583 deletions packages/api/src/dataFeedsRouter.json

Large diffs are not rendered by default.

41 changes: 33 additions & 8 deletions packages/api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,29 @@ import { MongoManager } from './database'
import { FeedRepository } from './repository/Feed'
import { ResultRequestRepository } from './repository/ResultRequest'
import { createServer } from './server'
import { Repositories, RouterDataFeedsConfig, NetworksConfig } from './types'
import { RouterDataFeedsConfig, Repositories, FeedInfo, NetworksConfig } from './types'
import { Web3Middleware } from './web3Middleware/index'
import { normalizeNetworkConfig } from './utils/index'
import {
normalizeAndValidateDataFeedConfig,
fetchDataFeedsRouterConfig
} from './readDataFeeds'
import { SvgCache } from './svgCache'
import { NetworkRouter } from './web3Middleware/NetworkRouter'
import { Configuration } from './web3Middleware/Configuration'

async function main () {
const svgCache = new SvgCache()
const mongoManager = new MongoManager()
const db = await mongoManager.start()
const dataFeedsRouterConfig: RouterDataFeedsConfig = await fetchDataFeedsRouterConfig()
const dataFeeds = normalizeAndValidateDataFeedConfig(dataFeedsRouterConfig)
const configurationFile: RouterDataFeedsConfig = await fetchDataFeedsRouterConfig()
const configuration = new Configuration(configurationFile)

const legacyFeeds: Array<FeedInfo> = normalizeAndValidateDataFeedConfig(configurationFile)
const networksConfigPartial: Array<Omit<
NetworksConfig,
'logo'
>> = normalizeNetworkConfig(dataFeedsRouterConfig)
>> = normalizeNetworkConfig(configurationFile)

const logosToFetch = networksConfigPartial.map(
(networksConfig: NetworksConfig) => {
Expand All @@ -40,19 +44,40 @@ async function main () {
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, repositories, networkInfo))


const newFeeds: Array<FeedInfo> = []

for (let router of routers) {
const feedInfos = await router.getFeedInfos()
newFeeds.concat(feedInfos)
}

const feeds = [...legacyFeeds, ...newFeeds]

const repositories: Repositories = {
feedRepository: new FeedRepository(dataFeeds),
resultRequestRepository: new ResultRequestRepository(db, dataFeeds)
feedRepository: new FeedRepository(feeds),
resultRequestRepository: new ResultRequestRepository(db, feeds)
}

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

web3Middleware.listen()

await createServer(repositories, svgCache, {
dataFeedsConfig: dataFeeds,
dataFeedsConfig: feeds,
networksConfig
})
}
Expand Down
33 changes: 31 additions & 2 deletions packages/api/src/repository/ResultRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class ResultRequestRepository {
collection: Collection<
ResultRequestDbObject | WithoutId<ResultRequestDbObject>
>
latestResults: Record<string, WithoutId<ResultRequestDbObject>> = {}

constructor (db: Db, _dataFeeds: Array<FeedInfo>) {
this.collection = db.collection('result_request')
Expand Down Expand Up @@ -78,8 +79,10 @@ export class ResultRequestRepository {
numericOrdering: true
}
}
)

).catch(e => {
console.log(`Error in getLastResult: ${feedFullName}`, e)
return null
})
return this.normalizeId(lastResultRequest)
}

Expand All @@ -89,6 +92,9 @@ export class ResultRequestRepository {
if (this.isValidResultRequest(resultRequest)) {
const response = await this.collection.insertOne(resultRequest)

// store in cache
this.latestResults[resultRequest.feedFullName] = resultRequest

return this.normalizeId(response[0])
} else {
console.error(
Expand All @@ -99,6 +105,29 @@ export class ResultRequestRepository {
}
}

async insertIfLatest (
resultRequest: WithoutId<ResultRequestDbObject>
): Promise<ResultRequestDbObjectNormalized | null> {

let storedResult = this.latestResults[resultRequest.feedFullName]
if (!storedResult) {
storedResult = await this.getLastResult(resultRequest.feedFullName)
}

const timestampChanged = storedResult?.timestamp !== resultRequest.timestamp

if (timestampChanged) {
return await this.insert(resultRequest)
} else {
console.log(
'Not inserting result because timestap is already inserted',
resultRequest
)
return null
}
}


private normalizeId (
resultRequest: ResultRequestDbObject
): ResultRequestDbObjectNormalized | null {
Expand Down
70 changes: 56 additions & 14 deletions packages/api/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,24 +42,64 @@ export type ConfigByFullName = {
}

export enum Network {
EthereumMainnet = 'ethereum-mainnet',
EthereumGoerli = 'ethereum-goerli',
EthereumRinkeby = 'ethereum-rinkeby',
ArbitrumOne = 'arbitrum-one' ,
ArbitrumGoerli = 'arbitrum-goerli',
AvalancheMainnet = 'avalanche-mainnet',
AvalancheFuji = 'avalanche-fuji',
BobaMainnet = 'boba-mainnet',
BobaRinkeby = 'boba-rinkeby',
ConfluxTethys = 'conflux-tethys',
ConfluxTestnet = 'conflux-testnet',
CeloMainnet = 'celo-mainnet',
BobaEthereumMainnet = 'boba-ethereum-mainnet',
BobaEthereumGoerli = 'boba-ethereum-goerli',
BobaBnbMainnet = 'boba-bnb-mainnet',
BobaBnbTestnet = 'boba-bnb-testnet',
CeloAlfajores = 'celo-alfajores',
HarmonyTestnet = 'harmony-testnet',
MetisMainnet = 'metis-mainnet',
MetisRinkeby = 'metis-rinkeby',
CeloMainnet = 'celo-mainnet',
ConfluxCoreMainnet = 'conflux-core-mainnet',
ConfluxEspaceMainnet = 'conflux-espace-mainnet',
ConfluxCoreTestnet = 'conflux-core-testnet',
ConfluxEspaceTestnet = 'conflux-espace-testnet',
CronosTestnet = 'cronos-testnet',
CronosMainnet = 'cronos-mainnet',
CubeTestnet = 'cube-testnet',
DogechainTestnet = 'dogechain-testnet',
DogechainMainnet = 'dogechain-mainnet',
ElastosMainnet = 'elastos-mainnet',
ElastosTestnet = 'elastos-testnet',
EthereumGoerli = 'ethereum-goerli',
EthereumSepolia = 'ethereum-sepolia',
EthereumMainnet = 'ethereum-mainnet',
FuseTestnet = 'fuse-testnet',
GnosisTestnet = 'gnosis-testnet',
KavaMainnet = 'kava-mainnet',
KavaTestnet = 'kava-testnet',
KccTestnet = 'kcc-testnet',
KccMainnet = 'kcc-mainnet',
KlaytnMainnet = 'klaytn-mainnet',
KlaytnTestnet = 'klaytn-testnet',
MantleTestnet = 'mantle-testnet',
MantleMainnet = 'mantle-mainnet',
MetisGoerli = 'metis-goerli',
MeterTestnet = 'meter-testnet',
MeterMainnet = 'meter-mainnet',
MoonbeamMainnet = 'moonbeam-mainnet',
MoonbeamMoonbase = 'moonbeam-moonbase',
PolygonMainnet = 'polygon-mainnet',
MoonbeamMoonriver = 'moonbeam-moonriver',
OkxX1Sepolia = 'okx-x1-sepolia',
OkxOkxchainTestnet = 'okx-okxchain-testnet',
OptimismGoerli = 'optimism-goerli',
OptimismMainnet = 'optimism-mainnet',
PolygonGoerli = 'polygon-goerli',
KCCMainnet = 'KCC-mainnet',
KCCTestnet = 'KCC-testnet'
PolygonMainnet = 'polygon-mainnet',
PolygonZkevmGoerli = 'polygon-zkevm-goerli',
PolygonZkevmMainnet = 'polygon-zkevm-mainnet',
ReefTestnet = 'reef-testnet',
ReefMainnet = 'reef-mainnet',
ScrollMainnet = 'scroll-mainnet',
ScrollSepolia = 'scroll-sepolia',
SmartbchAmber = 'smartbch-amber',
SyscoinTestnet = 'syscoin-testnet',
SyscoinMainnet = 'syscoin-mainnet',
SyscoinRolluxTestnet = 'syscoin-rollux-testnet',
UltronTestnet = 'ultron-testnet',
UltronMainnet = 'ultron-mainnet'
}

export type FeedInfoGeneric<ABI> = {
Expand Down Expand Up @@ -155,6 +195,7 @@ export type FeedParsedParams = {
}

export type FeedConfig = {
legacy?: boolean,
address: string
blockExplorer: string
hide?: boolean
Expand Down Expand Up @@ -188,6 +229,7 @@ export type NetworkConfigMap = Record<string, FeedConfig>
export type RouterDataFeedsConfig = {
abi: string
chains: Record<string, Chain>
feeds: FeedInfoRouterConfigMap
}

export type FeedInfosWithoutAbis = Array<
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function createFeedFullName (network, name, decimals) {
}

// Get networks list by chain
function getNetworksListByChain (config: RouterDataFeedsConfig) {
export function getNetworksListByChain (config: RouterDataFeedsConfig) {
return Object.values(config.chains).map(chain => {
const networkNames = Object.keys(chain.networks)
return Object.values(chain.networks).map((network, index) => {
Expand Down
99 changes: 99 additions & 0 deletions packages/api/src/web3Middleware/Configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { FeedParamsConfig, Network, NetworksConfig, RouterDataFeedsConfig } from "../types";
import { getNetworksListByChain, sortAlphabeticallyByLabel } from "../utils";
import { NetworkInfo } from "./NetworkRouter";
import { getProvider } from "./provider";

export class Configuration {
private configurationFile: RouterDataFeedsConfig

constructor(json: RouterDataFeedsConfig) {
this.configurationFile = json
}

// normalize config to fit network schema
public normalizeNetworkConfig (
config: RouterDataFeedsConfig
): Array<Omit<NetworksConfig, 'logo'>> {
// Get a list of networks where every element of the array contains another array with networks that belong to a chain.
const networks = getNetworksListByChain(config)

// Put all networks at the same level removing the nested arrays
const networkConfig = networks.reduce((networks, network) => {
network.map(network => {
networks.push({
...network
})
})
return networks
}, [])
const testnetNetworks = networkConfig.filter(network => !network.mainnet)
const mainnetNetworks = networkConfig.filter(network => network.mainnet)
return [
...sortAlphabeticallyByLabel(mainnetNetworks),
...sortAlphabeticallyByLabel(testnetNetworks)
]
}

// return networks using the new price feeds router contract
public listNetworksUsingPriceFeedsContract(): Array<NetworkInfo> {
return Object.values(this.configurationFile.chains)
.flatMap(chain => Object.entries(chain.networks))
.filter(([_, network])=> network.legacy === false)
.map(([networkKey, network]) => {
return {
provider: getProvider(networkKey.replaceAll('.', '-') as Network),
address: network.address,
pollingPeriod: network.pollingPeriod,
key: this.fromNetworkKeyToNetwork(networkKey),
networkName: network.name,
}
})
}

public getLegacyConfigurationFile(): RouterDataFeedsConfig {
const chains = Object.entries(this.configurationFile.chains).reduce((acc, [chainKey, chain]) => {

if (chain.hide) {
return acc
}

const networks = Object.entries(chain.networks).reduce((accNetworks, [networkKey, network]) => {
// add the network entry if it's legacy
return network.legacy ? { ...accNetworks, [networkKey]: network } : accNetworks;
}, {})

return Object.keys(networks).length > 0 ? { ...acc, [chainKey]: { ...chain, networks } } : acc
}, {})

return {
...this.configurationFile,
chains,
}
}

public getFeedConfiguration(priceFeedName: string, network: Network): FeedParamsConfig {
const defaultFeed = this.configurationFile.feeds[priceFeedName]
const specificFeedConfiguration = this.getNetworkConfiguration(network).feeds[priceFeedName]

return { ...defaultFeed, ...specificFeedConfiguration }
}

public isFeedActive(caption: string): boolean {
return Object.keys(this.configurationFile.feeds).includes(caption)
}

public getNetworkConfiguration(network: Network) {
return this.configurationFile.chains[getChain(network)].networks[networkToKey(network)]
}

private fromNetworkKeyToNetwork(networkKey: string): Network {
return networkKey.replace('.', '-') as Network
}
}

export function getChain(network: Network) {
return network.split('-')[0]
}
export function networkToKey(network: Network) {
return network.replaceAll('-', '.')
}
Loading

0 comments on commit 011d4a9

Please sign in to comment.