322 lines
13 KiB
JavaScript
Vendored
322 lines
13 KiB
JavaScript
Vendored
"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);
|
|
const { nativeCurrency, routerContract, echoContract, registryContract, aggregatorContract, reverseRecordsContract, governanceContract, multicallContract, offchainOracleContract, ovmGasPriceOracleContract, deployedBlock, constants: { GOVERNANCE_BLOCK, REGISTRY_BLOCK, NOTE_ACCOUNT_BLOCK, ENCRYPTED_NOTES_BLOCK }, } = config;
|
|
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,
|
|
});
|
|
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,
|
|
});
|
|
}
|
|
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: {},
|
|
tornado: {},
|
|
};
|
|
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})`);
|
|
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})`);
|
|
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;
|