Skip to content

Commit

Permalink
core: On-chain Price storage updating (#3)
Browse files Browse the repository at this point in the history
* onchain price setting

* added a logger to the project

* updated getAllTicker log event

* linter fixes
  • Loading branch information
pajicf authored Dec 14, 2023
1 parent 40d3099 commit 81a17ad
Show file tree
Hide file tree
Showing 15 changed files with 147 additions and 37 deletions.
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

0 comments on commit 81a17ad

Please sign in to comment.