Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: On-chain Price storage updating #3

Merged
merged 4 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"dotenv": "^16.3.1",
"ethers": "^6.9.0",
"express": "^4.18.2",
"log4js": "^6.9.1",
"node-cron": "^3.0.3",
"redux": "^5.0.0"
}
Expand Down
2 changes: 1 addition & 1 deletion src/constants/coingecko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const chainlinkTickerToCoingeckoMap: {[symbol: string]: string} = {
"ETH": "ethereum",
"LINK": "chainlink",
"SNX": "havven"
}
};

export function getCoingeckoIdByChainlinkTicker(ticker: string) {
return chainlinkTickerToCoingeckoMap[ticker];
Expand Down
5 changes: 3 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import router from "./app";
import { createServer, Server } from "http";
import logger from "./utils/logger.util";

const server: Server = createServer(router);

Expand All @@ -8,8 +9,8 @@ server.listen(router.get("port"), router.get("host"), async () => {
process.exit();
});

console.log(`Server started on ${router.get("host")}:${router.get("port")}`);
console.log("Press CTRL-C to stop\n");
logger.log(`Server started on ${router.get("host")}:${router.get("port")}`);
logger.log("Press CTRL-C to stop\n");
});

export default server;
6 changes: 4 additions & 2 deletions src/jobs/app.jobs.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { initTickerState, setupTickerFetchingJob } from "./ticker.jobs";
import {setupOffchainPriceFetchingJob} from "./price.jobs";
import { setupOffchainPriceFetchingJob } from "./price.jobs";
import logger from "../utils/logger.util";

const initAppState = async () => {
logger.log("Initialising the app state");
await initTickerState();
};

export const initApp = async () => {
await initAppState();

await setupTickerFetchingJob();
await setupOffchainPriceFetchingJob()
await setupOffchainPriceFetchingJob();
};
18 changes: 12 additions & 6 deletions src/jobs/price.jobs.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
import CoinGeckoService from "../services/coingecko/coingecko.service";
import CronService from "../services/cron.service";
import store from "../redux/store";
import {getCoingeckoIdByChainlinkTicker} from "../constants/coingecko";
import {setCurrentOffchainPrice} from "../redux/prices/prices.redux.actions";
import { getCoingeckoIdByChainlinkTicker } from "../constants/coingecko";
import { setCurrentOffchainPrice } from "../redux/prices/prices.redux.actions";
import OracleService from "../services/oracle/oracle.service";

export const setupOffchainPriceFetchingJob = async () => {
const coinGecko = new CoinGeckoService();
const oracleService = new OracleService();

CronService.scheduleRecurringJob(async () => {
const tickerSymbols = store.getState().tickers.symbols;
const tickerCoinGeckoIds = tickerSymbols.map(getCoingeckoIdByChainlinkTicker);

const offchainPrices = await coinGecko.getPrices(tickerCoinGeckoIds);
tickerCoinGeckoIds.forEach((id, index) => {
for (let index = 0; index < tickerSymbols.length; index++) {
const id = tickerCoinGeckoIds[index];
const symbol = tickerSymbols[index];
const currentPrice = offchainPrices[id]?.usd;

if (currentPrice) {
store.dispatch(setCurrentOffchainPrice(symbol, currentPrice));
}
})
})
}
try {
await oracleService.updateOnchainPrice(symbol, currentPrice);
} catch (err) { console.log(err);}
}
});
};
10 changes: 9 additions & 1 deletion src/jobs/ticker.jobs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import TickersService from "../services/tickers/tickers.service";
import store from "../redux/store";
import { addTicker } from "../redux/tickers/tickers.redux.actions";
import { TickerRegistryData } from "../services/tickers/tickers.service.types";
import logger from "../utils/logger.util";

const tickersService = new TickersService();

Expand All @@ -10,10 +11,17 @@ const updateReduxTickerState = (tickerData: TickerRegistryData) => {
};

export const initTickerState = async () => {
logger.log("Fetching all currently available tickers");
const tickers = await tickersService.getAllTickers();
tickers.forEach(updateReduxTickerState);
const symbols: string[] = [];
tickers.forEach(tickerData => {
updateReduxTickerState(tickerData);
symbols.push(tickerData.tickerSymbol);
});
logger.log("Tickers found: ", symbols);
};

export const setupTickerFetchingJob = async () => {
logger.log("Setting up the Ticker Fetching observer");
tickersService.listenForTickerUpdates(updateReduxTickerState).then();
};
6 changes: 3 additions & 3 deletions src/redux/prices/prices.redux.actions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {EPricesReduxActions, SetCurrentOffchainPriceAction, SetCurrentOnchainPriceAction} from "./prices.redux.types";
import { EPricesReduxActions, SetCurrentOffchainPriceAction, SetCurrentOnchainPriceAction } from "./prices.redux.types";

export function setCurrentOnchainPrice(tickerSymbol: string, onchainPrice: number): SetCurrentOnchainPriceAction {
return {
Expand All @@ -7,7 +7,7 @@ export function setCurrentOnchainPrice(tickerSymbol: string, onchainPrice: numbe
tickerSymbol,
onchainPrice
}
}
};
}

export function setCurrentOffchainPrice(tickerSymbol: string, offchainPrice: number): SetCurrentOffchainPriceAction {
Expand All @@ -17,5 +17,5 @@ export function setCurrentOffchainPrice(tickerSymbol: string, offchainPrice: num
tickerSymbol,
offchainPrice
}
}
};
}
12 changes: 6 additions & 6 deletions src/redux/prices/prices.redux.reducer.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {EPricesReduxActions, PricesReduxActions, PricesReduxReducerState} from "./prices.redux.types";
import {Reducer} from "redux";
import { EPricesReduxActions, PricesReduxActions, PricesReduxReducerState } from "./prices.redux.types";
import { Reducer } from "redux";

const initialState: PricesReduxReducerState = {
current: {}
}
};

const pricesReduxReducer: Reducer<PricesReduxReducerState, PricesReduxActions> = (state = initialState, action) => {
switch (action.type) {
Expand All @@ -19,7 +19,7 @@ const pricesReduxReducer: Reducer<PricesReduxReducerState, PricesReduxActions> =
onchain: onchainPrice
}
}
}
};
}
case EPricesReduxActions.SET_CURRENT_OFFCHAIN_PRICE: {
const { tickerSymbol, offchainPrice } = action.payload;
Expand All @@ -33,12 +33,12 @@ const pricesReduxReducer: Reducer<PricesReduxReducerState, PricesReduxActions> =
offchain: offchainPrice
}
}
}
};
}
default: {
return initialState;
}
}
}
};

export default pricesReduxReducer;
2 changes: 1 addition & 1 deletion src/redux/prices/prices.redux.types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {ReduxAction} from "../redux.types";
import { ReduxAction } from "../redux.types";

export enum EPricesReduxActions {
SET_CURRENT_ONCHAIN_PRICE = "SET_CURRENT_ONCHAIN_PRICE",
Expand Down
41 changes: 41 additions & 0 deletions src/services/oracle/oracle.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import Web3Service from "../web3.service";
import { CONFIG } from "../../config";
import logger from "../../utils/logger.util";

class OracleService {
private _tickerPriceContract;

constructor() {
this._tickerPriceContract = Web3Service.getTickerPriceStorageContract(CONFIG.TICKER_PRICE_STORAGE, true);
}

public async updateOnchainPrice(ticker: string, newPrice: number) {
const numberOfChainlinkDecimals = 8;
// @TODO Update to use BigInt
const parsedPrice = Math.trunc(newPrice * 10**numberOfChainlinkDecimals);
logger.log("Trying to update onchain price of ", ticker, " to ", newPrice);

const willRevert = await this.isPriceUpdateGoingToRevert(ticker, parsedPrice);

if(!willRevert) {
const tx = await this._tickerPriceContract.set(ticker, parsedPrice);
logger.info(`Transaction for updating ${ticker} to ${newPrice} sent, tx hash: ${tx.hash}`);
await tx.wait(1);
logger.info(`Transaction with tx hash: ${tx.hash} successful`);
} else {
logger.info(`Updating ticker ${ticker} to price ${newPrice} canceled`);
}
}

public async isPriceUpdateGoingToRevert(ticker: string, newPrice: number): Promise<boolean> {
try {
await this._tickerPriceContract.set.staticCall(ticker, newPrice);

return false;
} catch (err) {
return true;
}
}
}

export default OracleService;
6 changes: 0 additions & 6 deletions src/services/prices/prices.service.ts

This file was deleted.

3 changes: 3 additions & 0 deletions src/services/tickers/tickers.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Web3Service from "../web3.service";
import { CONFIG } from "../../config";
import { TypedEventLog } from "../../contracts/common";
import { TickerRegistryData } from "./tickers.service.types";
import logger from "../../utils/logger.util";

class TickersService {
private _registryContract;
Expand All @@ -21,6 +22,8 @@ class TickersService {
}

const parsedEvent = this.parseTickerFeedUpdatedEvent(data);
logger.log("New ticker found: ", parsedEvent);

onTickerUpdate(parsedEvent);
});
}
Expand Down
16 changes: 9 additions & 7 deletions src/services/web3.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ethers, JsonRpcProvider, Wallet } from "ethers";
import { TickerUSDFeedRegistryAbi__factory } from "../contracts";
import { TickerPriceStorageAbi__factory, TickerUSDFeedRegistryAbi__factory } from "../contracts";

import { CONFIG } from "../config";

Expand All @@ -12,14 +12,16 @@ class Web3Service {
this.signer = new ethers.Wallet(signerPrivateKey, this.provider);
}

private _getRunner(isMutatingState: boolean) {
return isMutatingState ? this.signer : this.provider;
}

public getTickerUSDFeedRegistryContract(address: string, isMutatingState?: boolean) {
const contract = TickerUSDFeedRegistryAbi__factory.connect(address);
return TickerUSDFeedRegistryAbi__factory.connect(address, this._getRunner(!!isMutatingState));
}

if (isMutatingState) {
return contract.connect(this.signer);
} else {
return contract.connect(this.provider);
}
public getTickerPriceStorageContract(address: string, isMutatingState?: boolean) {
return TickerPriceStorageAbi__factory.connect(address, this._getRunner(!!isMutatingState));
}
}

Expand Down
13 changes: 13 additions & 0 deletions src/utils/logger.util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as log4js from "log4js";
log4js.configure({
appenders: {
out: { type: "stdout" }
},
categories: {
default: { appenders: ["out"], level: "all" }
}
});

const logger = log4js.getLogger();

export default logger;
43 changes: 41 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,11 @@ cross-spawn@^7.0.2:
shebang-command "^2.0.0"
which "^2.0.1"

date-format@^4.0.14:
version "4.0.14"
resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.14.tgz#7a8e584434fb169a521c8b7aa481f355810d9400"
integrity sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==

[email protected]:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
Expand Down Expand Up @@ -1007,7 +1012,7 @@ flat-cache@^3.0.4:
keyv "^4.5.3"
rimraf "^3.0.2"

flatted@^3.2.9:
flatted@^3.2.7, flatted@^3.2.9:
version "3.2.9"
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf"
integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==
Expand Down Expand Up @@ -1045,6 +1050,15 @@ fs-extra@^7.0.0:
jsonfile "^4.0.0"
universalify "^0.1.0"

fs-extra@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^4.0.0"
universalify "^0.1.0"

fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
Expand Down Expand Up @@ -1134,7 +1148,7 @@ gopd@^1.0.1:
dependencies:
get-intrinsic "^1.1.3"

graceful-fs@^4.1.2, graceful-fs@^4.1.6:
graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
Expand Down Expand Up @@ -1342,6 +1356,17 @@ lodash@^4.17.15:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==

log4js@^6.9.1:
version "6.9.1"
resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.9.1.tgz#aba5a3ff4e7872ae34f8b4c533706753709e38b6"
integrity sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==
dependencies:
date-format "^4.0.14"
debug "^4.3.4"
flatted "^3.2.7"
rfdc "^1.3.0"
streamroller "^3.1.5"

lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
Expand Down Expand Up @@ -1660,6 +1685,11 @@ reusify@^1.0.4:
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==

rfdc@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b"
integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==

rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
Expand Down Expand Up @@ -1773,6 +1803,15 @@ [email protected]:
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==

streamroller@^3.1.5:
version "3.1.5"
resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.1.5.tgz#1263182329a45def1ffaef58d31b15d13d2ee7ff"
integrity sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==
dependencies:
date-format "^4.0.14"
debug "^4.3.4"
fs-extra "^8.1.0"

string-format@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/string-format/-/string-format-2.0.0.tgz#f2df2e7097440d3b65de31b6d40d54c96eaffb9b"
Expand Down
Loading