Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Tommytrg committed Dec 23, 2024
1 parent 9855c51 commit c40fa60
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 233 deletions.
16 changes: 0 additions & 16 deletions packages/api/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,22 +50,6 @@ export function normalizeNetworkConfig(
]
}

export function isZeroAddress(address: string) {
return address === '0x0000000000000000000000000000000000000000' || !address
}

export function isZeroHash(hash: string) {
return (
hash ===
'0x0000000000000000000000000000000000000000000000000000000000000000' ||
!hash
)
}

export function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}

export function removeRepeatedElements<T>(items: Array<T>): Array<T> {
return [...new Set(items)]
}
7 changes: 6 additions & 1 deletion packages/api/src/web3Middleware/NetworkRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export class NetworkRouter {
private configuration: Configuration
private provider: string
private lastSupportedFeedsID = ''
private interval

constructor(
configuration: Configuration,
Expand Down Expand Up @@ -88,7 +89,7 @@ export class NetworkRouter {

// Periodically fetch the price feed router contract and store it in mongodb
public listen() {
setInterval(async () => {
this.interval = setInterval(async () => {
const snapshot = await this.getSnapshot()
const insertPromises = snapshot.feeds
.filter((feed) => isFeedWithPrice(feed) && feed.timestamp !== '0')
Expand All @@ -114,6 +115,10 @@ export class NetworkRouter {
}, this.pollingPeriod)
}

public stop() {
clearInterval(this.interval)
}

async getSnapshot(): Promise<NetworkSnapshot | PartialNetworkSnapshot> {
const supportedFeeds = await this.getSupportedFeeds()

Expand Down
215 changes: 15 additions & 200 deletions packages/api/src/web3Middleware/index.ts
Original file line number Diff line number Diff line change
@@ -1,225 +1,40 @@
import Web3 from 'web3'
import { toHex } from 'web3-utils'
import {
Contracts,
ContractsState,
FeedInfo,
Repositories,
ContractInfo,
} from '../../types'
import { isZeroAddress } from '../utils/index'
import { getProvider } from './provider'
import { Repositories } from '../../types'
import { NetworkRouter } from './NetworkRouter'
import { Configuration } from './Configuration'

export class Web3Middleware {
public repositories: Repositories
private Web3: typeof Web3
public routerContractByNetwork: Record<string, any> = {}
public contractIdByFeedId: Record<string, string> = {}
// feedFullname -> address
public currentFeedAddresses: Record<string, string> = {}
public networkRouters: Array<NetworkRouter>
public networkRouters: Array<NetworkRouter> = []
public configuration: Configuration

private intervals: Array<ReturnType<typeof setInterval>> = []

constructor(
configuration: Configuration,
dependencies: { Web3: typeof Web3; repositories: Repositories },
) {
this.repositories = dependencies.repositories
this.Web3 = dependencies.Web3
this.configuration = configuration
}

public listen() {
this.listenWitnetPriceFeeds()
}

public async listenWitnetPriceFeeds() {
this.configuration
this.networkRouters = this.configuration
.listNetworksUsingPriceFeedsContract()
.forEach((networkInfo) =>
new NetworkRouter(
this.configuration,
this.Web3,
this.repositories,
networkInfo,
).listen(),
.map(
(networkInfo) =>
new NetworkRouter(
this.configuration,
this.Web3,
this.repositories,
networkInfo,
),
)
}

async recheckFeedAddress(feedInfo: FeedInfo) {
const contractInfo = await this.getContractInfo(feedInfo)
const feed = this.repositories.feedRepository.get(feedInfo.feedFullName)

if (
contractInfo?.contractAddress &&
contractInfo?.contractAddress !== feed?.address
) {
console.log(
`Address of ${feedInfo.feedFullName}: ${feed.address} -> ${contractInfo.contractAddress}`,
)

this.currentFeedAddresses[feed.feedFullName] =
contractInfo.contractAddress
}

return feedInfo
public async listen() {
this.networkRouters.map((networkRouter) => networkRouter.listen())
}

stop() {
this.intervals.forEach((interval) => {
clearInterval(interval)
})

this.intervals = []
}

async getContractInfo(feedInfo: FeedInfo): Promise<ContractInfo | null> {
try {
return await new Promise(async (resolve, reject) => {
try {
const provider = getProvider(feedInfo.network)
const timeout = 30000
let web3: Web3 | undefined
//FIXME: make timeout work
if (provider) {
web3 = new this.Web3(new Web3.providers.HttpProvider(provider))
}
//FIXME: use web3 timeout instead of custom
setTimeout(() => {
reject('Timeout')
}, timeout)

if (web3 && !this.routerContractByNetwork[feedInfo.network]) {
this.routerContractByNetwork[feedInfo.network] =
new web3.eth.Contract(
feedInfo.routerAbi as any,
feedInfo.routerAddress,
)
}
const routerContract = this.routerContractByNetwork[feedInfo.network]

if (!this.contractIdByFeedId[feedInfo.id]) {
this.contractIdByFeedId[feedInfo.id] = await routerContract.methods
.currencyPairId(feedInfo.id)
.call()
}
const contractIdentifier = this.contractIdByFeedId[feedInfo.id]
const address = await routerContract.methods
.getPriceFeed(contractIdentifier)
.call()
resolve({
contractAddress: address,
contractId: contractIdentifier,
})
} catch (err) {
reject(err)
}
})
} catch (err) {
console.log(
`Error reading pricefeed contract address for ${feedInfo.feedFullName}: ${err}`,
)
return null
}
}

async readDataFeedContract(feedInfo: FeedInfo, provider) {
const web3 = new this.Web3(provider)
const contractAddress = this.currentFeedAddresses[feedInfo.feedFullName]
if (contractAddress && !isZeroAddress(contractAddress)) {
const feedContract = new web3.eth.Contract(
feedInfo.abi as any,
contractAddress,
)
console.log(
`Reading ${feedInfo.feedFullName} contract state at address: ${contractAddress}`,
)
await this.fetchAndSaveContractSnapshot(
{ feedContract },
feedInfo.feedFullName,
)
} else {
console.error(`Pricefeed address not set for ${feedInfo.feedFullName}`)
}
}

async listenToDataFeed(feedInfo: FeedInfo) {
const provider = getProvider(feedInfo.network)
if (provider) {
try {
this.readDataFeedContract(feedInfo, provider)
const interval = setInterval(async () => {
this.readDataFeedContract(feedInfo, provider)
}, feedInfo.pollingPeriod)

this.intervals.push(interval)
} catch (err) {
console.error(`Provider not valid for ${feedInfo.network}`, err)
}
} else {
console.error(`Provider not set for network ${feedInfo.network}`)
}

return
}

async readContractsState({ feedContract }: Contracts, feedFullName: string) {
try {
const { _lastPrice, _lastTimestamp, _lastDrTxHash, _latestUpdateStatus } =
await feedContract.methods.lastValue().call()
console.log(
`Latest contract update status for ${feedFullName}`,
_latestUpdateStatus,
)
const requestId = await feedContract.methods.latestQueryId().call()
return {
lastPrice: _lastPrice.toString(),
lastTimestamp: _lastTimestamp.toString(),
lastDrTxHash: _lastDrTxHash.toString(),
requestId: requestId.toString(),
}
} catch (err) {
throw new Error(`Error reading contract state for ${feedFullName} ${err}`)
}
}

async fetchAndSaveContractSnapshot(
contracts: Contracts,
feedFullName: string,
) {
return new Promise(async (resolve) => {
try {
setTimeout(() => {
console.log(`Timeout while reading from ${feedFullName}`)
resolve(true)
}, 30000)
const {
lastPrice,
lastTimestamp,
lastDrTxHash,
requestId,
}: ContractsState = await this.readContractsState(
contracts,
feedFullName,
)
await this.repositories.resultRequestRepository.insertIfLatest({
result: lastPrice,
timestamp: lastTimestamp,
requestId: requestId,
drTxHash: toHex(lastDrTxHash).slice(2),
feedFullName,
})
resolve(true)
} catch (error) {
console.error(
`Error reading contracts state for ${feedFullName}:`,
error,
)
}
})
public stop() {
this.networkRouters.forEach((networkRouter) => networkRouter.stop())
}
}
16 changes: 0 additions & 16 deletions packages/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,6 @@ export type PaginatedFeedsObject = {
total: number
}

export type ContractInfo = {
contractAddress: string
contractId: string
}

export type ResultRequestDbObjectNormalized = ResultRequestDbObject & {
id: string
}
Expand All @@ -153,17 +148,6 @@ export type Repositories = {
resultRequestRepository: ResultRequestRepository
}

export type ContractsState = {
lastPrice: string
lastTimestamp: string
lastDrTxHash: string
requestId: string
}

export type Contracts = {
feedContract: any
}

export type FeedInfoRouterConfigMap = {
[key: string]: FeedParamsConfig
}
Expand Down

0 comments on commit c40fa60

Please sign in to comment.