-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
1,510 additions
and
28 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import Web3 from "web3" | ||
import WitnetPriceFeedsABI from './../abi/WitnetPriceFeeds.json' | ||
import { AbiItem, Repositories } from "./../types" | ||
import { toHex } from "web3-utils" | ||
import { createFeedFullName } from "../utils" | ||
|
||
|
||
enum ResultStatus { | ||
Void = 0, | ||
Awaiting = 1, | ||
Ready = 2, | ||
Error = 3, | ||
AwaitingReady = 4, | ||
AwaitingError = 5 | ||
} | ||
|
||
type SupportedFeed = { | ||
id: string, | ||
caption: string | ||
solver: string | ||
} | ||
|
||
type LatestPrice = { | ||
value: string, | ||
timestamp: string, | ||
tallyHash: string, | ||
status: ResultStatus | ||
} | ||
|
||
export type NetworkInfo = { | ||
name: string, | ||
provider: string, | ||
address: string, | ||
pollingPeriod: number, | ||
maxSecsBetweenUpdates: number, | ||
feeds?: Array<{ name: string, maxSecsBetweenUpdates: number}> | ||
} | ||
export type NetworkSnapshot = { | ||
network: string, | ||
feeds: Array<SupportedFeed & LatestPrice> | ||
} | ||
|
||
export class NetworkRouter { | ||
public contract: any | ||
public network: string | ||
public pollingPeriod: number | ||
public maxSecsBetweenUpdates: number | ||
public feeds?: Array<{ name: string; maxSecsBetweenUpdates: number }> | ||
|
||
public repositories: Repositories | ||
|
||
constructor(repositories: Repositories, networkInfo: NetworkInfo) { | ||
const { name, provider, address, pollingPeriod, maxSecsBetweenUpdates, feeds } = networkInfo | ||
|
||
const web3 = new Web3(new Web3.providers.HttpProvider(provider, { timeout: 30000 })) | ||
this.contract = new web3.eth.Contract( WitnetPriceFeedsABI as unknown as AbiItem, address) | ||
this.network = name | ||
this.pollingPeriod = pollingPeriod, | ||
this.maxSecsBetweenUpdates = maxSecsBetweenUpdates | ||
this.feeds = feeds | ||
this.repositories = repositories | ||
} | ||
|
||
// Periodically fetch the price feed router contract and store it in mongodb | ||
public listen() { | ||
setInterval(async () => { | ||
const snapshot = await this.getSnapshot() | ||
console.log("Last contract snapshot", snapshot) | ||
const insertPromises = snapshot.feeds.filter(feed => feed.status !== ResultStatus.Ready).map((feed) => ({ | ||
feedFullName: createFeedFullName(this.network, feed.caption, feed.caption.split("-").reverse()[0]), | ||
drTxHash: toHex(feed.tallyHash).slice(2), | ||
requestId: feed.id, | ||
result: feed.value, | ||
timestamp: feed.timestamp | ||
})) | ||
.map(this.repositories.resultRequestRepository.insertIfLatest) | ||
|
||
Promise.all(insertPromises) | ||
}, this.pollingPeriod) | ||
} | ||
|
||
async getSnapshot(): Promise<NetworkSnapshot> { | ||
const supportedFeeds = await this.getSupportedFeeds() | ||
const feedIds = supportedFeeds.map(feed => feed.id) | ||
const latestPrices = await this.latestPrices(feedIds) | ||
|
||
return { | ||
network: this.network, | ||
feeds: supportedFeeds.map((supportedFeed, index) => ({ | ||
...supportedFeed, | ||
...latestPrices[index] | ||
})) | ||
} | ||
} | ||
|
||
// Wrap supportedFeeds contract method | ||
private async getSupportedFeeds (): Promise<Array<SupportedFeed>> { | ||
const supportedFeeds = await this.contract.methods.supportedFeeds().call() | ||
|
||
return supportedFeeds._ids.map((_, index) => ({ | ||
id: supportedFeeds._ids[index], | ||
caption: supportedFeeds._captions[index], | ||
solver: supportedFeeds._solvers[index], | ||
})) | ||
} | ||
|
||
// Wrap latestPrices contract method | ||
private async latestPrices (ids: Array<string>): Promise<Array<LatestPrice>> { | ||
const latestPrices = await this.contract.methods.latestPrices(ids).call() | ||
return latestPrices.map(latestPrice => ({ | ||
value: latestPrice.value.toString(), | ||
timestamp: latestPrice.timestamp.toString(), | ||
tallyHash: latestPrice.tallyHash, | ||
status: Number(latestPrice.status) | ||
})) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { Repositories } from '../../src/types.js' | ||
import { NetworkRouter } from '../../src/web3Middleware/NetworkRouter' | ||
|
||
|
||
describe('NetworkRouter', () => { | ||
it('should fetch network contract', async () => { | ||
// FIXME: create a proper mock | ||
const repositories = { feedRepository: { }, resultRequestRepository: { } } as unknown as Repositories | ||
const networkInfo = { | ||
address:'0x9999999d139bdBFbF25923ba39F63bBFc7593400', | ||
provider: 'https://rpc2.sepolia.org', | ||
name: 'ethereum.sepholia', | ||
pollingPeriod: 1, | ||
maxSecsBetweenUpdates: 1 | ||
} | ||
const router = new NetworkRouter(repositories, networkInfo) | ||
|
||
const snapshot = await router.getSnapshot() | ||
|
||
expect(snapshot.feeds[0].caption).toBeTruthy() | ||
expect(snapshot.feeds[0].id).toBeTruthy() | ||
expect(snapshot.feeds[0].solver).toBeTruthy() | ||
expect(snapshot.feeds[0].status).toBeTruthy() | ||
expect(snapshot.feeds[0].tallyHash).toBeTruthy() | ||
expect(snapshot.feeds[0].timestamp).toBeTruthy() | ||
expect(snapshot.feeds[0].value).toBeTruthy() | ||
|
||
expect(snapshot.network).toBe('ethereum.sepholia') | ||
}) | ||
}) |