tovarish-relayer/lib/services/sync.js

322 lines
13 KiB
JavaScript
Raw Permalink Normal View History

2024-11-13 04:42:13 +03:00
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SyncManager = void 0;
exports.syncGasPrice = syncGasPrice;
exports.syncPrices = syncPrices;
exports.syncNetworkEvents = syncNetworkEvents;
const contracts_1 = require("@tornado/contracts");
const core_1 = require("@tornado/core");
const logger_1 = require("./logger");
const treeCache_1 = require("./treeCache");
const events_1 = require("./events");
const error_1 = require("./error");
function setupServices(syncManager) {
const { relayerConfig, logger, syncStatus } = syncManager;
const { cacheDir: cacheDirectory, userEventsDir: userDirectory, userTreeDir, merkleWorkerPath, enabledNetworks, } = relayerConfig;
const cachedServices = {};
for (const netId of enabledNetworks) {
const config = (0, core_1.getConfig)(netId);
const rpcUrl = relayerConfig.rpcUrls[netId];
const provider = (0, core_1.getProviderWithNetId)(netId, rpcUrl, config);
2024-11-19 13:22:27 +03:00
const { nativeCurrency, routerContract, echoContract, registryContract, aggregatorContract, reverseRecordsContract, governanceContract, multicallContract, offchainOracleContract, ovmGasPriceOracleContract, deployedBlock, constants: { GOVERNANCE_BLOCK, REGISTRY_BLOCK, NOTE_ACCOUNT_BLOCK, ENCRYPTED_NOTES_BLOCK }, } = config;
2024-11-13 04:42:13 +03:00
if (!syncStatus[netId]) {
syncStatus[netId] = {
events: false,
tokenPrice: false,
gasPrice: false,
};
}
const services = (cachedServices[netId] = {});
services.provider = provider;
services.tokenPriceOracle = new core_1.TokenPriceOracle(provider, core_1.Multicall__factory.connect(multicallContract, provider), offchainOracleContract ? core_1.OffchainOracle__factory.connect(offchainOracleContract, provider) : undefined);
services.tornadoFeeOracle = new core_1.TornadoFeeOracle(provider, ovmGasPriceOracleContract
? core_1.OvmGasPriceOracle__factory.connect(ovmGasPriceOracleContract, provider)
: undefined);
if (governanceContract && aggregatorContract && reverseRecordsContract) {
services.governanceService = new events_1.NodeGovernanceService({
netId,
provider,
Governance: contracts_1.Governance__factory.connect(governanceContract, provider),
Aggregator: contracts_1.Aggregator__factory.connect(aggregatorContract, provider),
ReverseRecords: core_1.ReverseRecords__factory.connect(reverseRecordsContract, provider),
deployedBlock: GOVERNANCE_BLOCK,
cacheDirectory,
userDirectory,
logger,
});
}
if (registryContract && aggregatorContract) {
services.registryService = new events_1.NodeRegistryService({
netId,
provider,
RelayerRegistry: contracts_1.RelayerRegistry__factory.connect(registryContract, provider),
Aggregator: contracts_1.Aggregator__factory.connect(aggregatorContract, provider),
relayerEnsSubdomains: (0, core_1.getRelayerEnsSubdomains)(),
deployedBlock: REGISTRY_BLOCK,
cacheDirectory,
userDirectory,
logger,
});
services.revenueService = new events_1.NodeRevenueService({
netId,
provider,
RelayerRegistry: contracts_1.RelayerRegistry__factory.connect(registryContract, provider),
deployedBlock: REGISTRY_BLOCK,
cacheDirectory,
userDirectory,
logger,
});
}
services.echoService = new events_1.NodeEchoService({
netId,
provider,
Echoer: contracts_1.Echoer__factory.connect(echoContract, provider),
deployedBlock: NOTE_ACCOUNT_BLOCK,
cacheDirectory,
userDirectory,
logger,
});
services.encryptedNotesService = new events_1.NodeEncryptedNotesService({
netId,
provider,
Router: contracts_1.TornadoRouter__factory.connect(routerContract, provider),
deployedBlock: ENCRYPTED_NOTES_BLOCK,
cacheDirectory,
userDirectory,
logger,
});
2024-11-19 13:22:27 +03:00
const instances = (0, core_1.getMultiInstances)(netId, config);
const [firstInstance, { amount, currency }] = Object.entries(instances)[0];
services.tornadoServices = new events_1.NodeMultiTornadoService({
netId,
provider,
instances,
deployedBlock,
merkleTreeService: new core_1.MerkleTreeService({
netId,
amount,
currency,
Tornado: contracts_1.Tornado__factory.connect(firstInstance, provider),
merkleWorkerPath,
}),
treeCache: new treeCache_1.TreeCache({
userDirectory: userTreeDir,
}),
optionalTree: true,
nativeCurrency,
cacheDirectory,
userDirectory,
logger,
});
2024-11-13 04:42:13 +03:00
}
syncManager.cachedServices = cachedServices;
}
async function syncGasPrice(syncManager, netId) {
const { cachedServices, logger, errors, cachedGasPrices, latestBlocks, latestBalances, syncStatus, relayerConfig: { rewardAccount }, } = syncManager;
try {
const services = cachedServices[netId];
const { provider, tornadoFeeOracle } = services;
const [blockNumber, balance, gasPrice, l1Fee] = await Promise.all([
provider.getBlockNumber(),
provider.getBalance(rewardAccount),
tornadoFeeOracle.gasPrice(),
tornadoFeeOracle.fetchL1OptimismFee(),
]);
cachedGasPrices[netId] = {
gasPrice: gasPrice.toString(),
l1Fee: l1Fee ? l1Fee.toString() : undefined,
};
latestBlocks[netId] = blockNumber;
latestBalances[netId] = balance.toString();
syncStatus[netId].gasPrice = true;
}
catch (err) {
logger.error(`${netId}: Failed to sync gas prices`);
console.log(err);
syncStatus[netId].gasPrice = false;
errors.push((0, error_1.newError)('SyncManager (gas)', netId, err));
}
}
async function syncPrices(syncManager, netId) {
const { cachedServices, logger, errors, cachedPrices, syncStatus } = syncManager;
try {
const config = (0, core_1.getConfig)(netId);
const { nativeCurrency, tornContract } = config;
const services = cachedServices[netId];
const { tokenPriceOracle } = services;
// Classic UI ajv validator requires all token prices to present
const allTokens = Object.keys(config.tokens);
if (tornContract && !allTokens.includes('torn')) {
allTokens.push('torn');
}
const tokens = allTokens
.map((currency) => {
if (currency === nativeCurrency) {
return;
}
if (currency === 'torn') {
return {
currency,
tokenAddress: tornContract,
decimals: 18,
};
}
const { tokenAddress, decimals } = config.tokens[currency];
return {
currency,
tokenAddress,
decimals,
};
})
.filter((t) => t);
if (!tokens.length) {
syncStatus[netId].tokenPrice = true;
return;
}
cachedPrices[netId] = (await tokenPriceOracle.fetchPrices(tokens)).reduce((acc, price, index) => {
acc[tokens[index].currency] = price;
return acc;
}, {});
syncStatus[netId].tokenPrice = true;
logger.info(`${netId}: Synced ${tokens.length} tokens price`);
}
catch (err) {
logger.error(`${netId}: Failed to sync prices`);
console.log(err);
syncStatus[netId].tokenPrice = false;
errors.push((0, error_1.newError)('SyncManager (price)', netId, err));
}
}
async function syncNetworkEvents(syncManager, netId) {
const { cachedEvents, cachedServices, logger, errors, syncStatus } = syncManager;
try {
const services = cachedServices[netId];
const { provider, governanceService, registryService, revenueService, echoService, encryptedNotesService, tornadoServices, } = services;
logger.info(`${netId}: Syncing events from block ${await provider.getBlockNumber()}`);
const eventsStatus = {
governance: governanceService ? {} : undefined,
registry: registryService ? {} : undefined,
revenue: revenueService ? {} : undefined,
echo: {},
encrypted_notes: {},
2024-11-19 13:22:27 +03:00
tornado: {},
2024-11-13 04:42:13 +03:00
};
if (governanceService) {
const { events, lastBlock } = await governanceService.updateEvents();
eventsStatus.governance = {
events: events.length,
lastBlock,
};
logger.info(`${netId}: Updated governance events (total: ${events.length}, block: ${lastBlock})`);
}
if (registryService) {
{
const { events, lastBlock } = await registryService.updateEvents();
eventsStatus.registry = {
events: events.length,
lastBlock,
};
logger.info(`${netId}: Updated registry events (total: ${events.length}, block: ${lastBlock})`);
}
{
const { lastBlock, timestamp, relayers } = await registryService.updateRelayers();
logger.info(`${netId}: Updated registry relayers (total: ${relayers.length}, block: ${lastBlock}, timestamp: ${timestamp})`);
}
}
if (revenueService) {
const { events, lastBlock } = await revenueService.updateEvents();
eventsStatus.revenue = {
events: events.length,
lastBlock,
};
logger.info(`${netId}: Updated revenue events (total: ${events.length}, block: ${lastBlock})`);
}
const echoEvents = await echoService.updateEvents();
eventsStatus.echo = {
events: echoEvents.events.length,
lastBlock: echoEvents.lastBlock,
};
logger.info(`${netId}: Updated echo events (total: ${echoEvents.events.length}, block: ${echoEvents.lastBlock})`);
const encryptedNotesEvents = await encryptedNotesService.updateEvents();
eventsStatus.encrypted_notes = {
events: encryptedNotesEvents.events.length,
lastBlock: encryptedNotesEvents.lastBlock,
};
logger.info(`${netId}: Updated encrypted notes events (total: ${encryptedNotesEvents.events.length}, block: ${encryptedNotesEvents.lastBlock})`);
2024-11-19 13:22:27 +03:00
const tornadoEvents = await tornadoServices.updateEvents();
eventsStatus.tornado = {
events: tornadoEvents.events.length,
lastBlock: tornadoEvents.lastBlock,
};
logger.info(`${netId}: Updated tornado events (total: ${tornadoEvents.events.length}, block: ${tornadoEvents.lastBlock})`);
2024-11-13 04:42:13 +03:00
cachedEvents[netId] = eventsStatus;
syncStatus[netId].events = true;
logger.info(`${netId}: Synced all events`);
await Promise.all([syncPrices(syncManager, netId), syncGasPrice(syncManager, netId)]);
}
catch (err) {
logger.error(`${netId}: Failed to sync events`);
console.log(err);
syncStatus[netId].events = false;
errors.push((0, error_1.newError)('SyncManager (events)', netId, err));
}
}
class SyncManager {
relayerConfig;
logger;
cachedServices;
cachedEvents;
cachedPrices;
cachedGasPrices;
syncStatus;
latestBlocks;
latestBalances;
errors;
onSyncEvents;
constructor(relayerConfig) {
this.relayerConfig = relayerConfig;
this.logger = (0, logger_1.getLogger)('[SyncManager]', relayerConfig.logLevel);
this.cachedServices = {};
this.cachedEvents = {};
this.cachedPrices = {};
this.cachedGasPrices = {};
this.syncStatus = {};
this.latestBlocks = {};
this.latestBalances = {};
this.errors = [];
this.onSyncEvents = false;
setupServices(this);
}
getStatus() {
return {
cachedEvents: this.cachedEvents,
cachedPrices: JSON.parse(JSON.stringify(this.cachedPrices)),
cachedGasPrices: JSON.parse(JSON.stringify(this.cachedGasPrices)),
syncStatus: this.syncStatus,
latestBlocks: this.latestBlocks,
latestBalances: this.latestBalances,
errors: this.errors.map(({ type, netId, timestamp }) => ({
type,
netId,
timestamp,
})),
onSyncEvents: this.onSyncEvents,
};
}
getPrice(netId, token) {
return this.cachedPrices[netId]?.[token] || BigInt(0);
}
getGasPrice(netId) {
return this.cachedGasPrices[netId];
}
async syncEvents() {
if (this.onSyncEvents) {
return;
}
this.onSyncEvents = true;
await Promise.all(this.relayerConfig.enabledNetworks.map((netId) => syncNetworkEvents(this, Number(netId))));
this.onSyncEvents = false;
}
}
exports.SyncManager = SyncManager;