diff --git a/src/app.ts b/src/app.ts index 23b1ca1..2714bc6 100644 --- a/src/app.ts +++ b/src/app.ts @@ -27,8 +27,8 @@ app.use(express.urlencoded({ extended: true })); app.use(cors()); app.options('*', cors()); -app.use(crons); app.use('/v1/api', router); +app.use(crons); // send back a 404 error for any unknown api request app.use((req: Request, res: Response, next: NextFunction) => { diff --git a/src/config/config.ts b/src/config/config.ts index c70bca0..e84c3de 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -13,6 +13,7 @@ const envVarsSchema = Joi.object() PORT: Joi.number().default(3000), MONGODB_URL: Joi.string().required().description('Mongo DB url'), RPC_URL: Joi.string().required().description('RPC URL'), + EXPLORER_URL: Joi.string().required().description('Explorer URL'), }) .unknown(); @@ -28,6 +29,7 @@ const config = { env: envVars.NODE_ENV, port: envVars.PORT, rpcUrl: envVars.RPC_URL, + explorerUrl: envVars.EXPLORER_URL, mongoose: { url: envVars.MONGODB_URL + (envVars.NODE_ENV === 'test' ? '-test' : ''), options: { diff --git a/src/models/quantumPortalBlock.model.ts b/src/models/quantumPortalBlock.model.ts index c8146e3..d795eb2 100644 --- a/src/models/quantumPortalBlock.model.ts +++ b/src/models/quantumPortalBlock.model.ts @@ -6,15 +6,16 @@ const quantumPortalBlockSchema = new Schema({ parentHash: String, number: Number, nonce: String, - timestamp: Number, - difficulty: Number, + timestamp: String, + difficulty: String, gasLimit: String, gasUsed: String, miner: String, - extraData: String, - transactions: [Object], baseFeePerGas: String, - _difficulty: String, + txCount: Number, + rewards: [Object], + txsFees: String, + totalDifficulty: String, }); export const QuantumPortalBlockModel = mongoose.model( diff --git a/src/models/quantumPortalTransaction.model.ts b/src/models/quantumPortalTransaction.model.ts index 0744907..73abf03 100644 --- a/src/models/quantumPortalTransaction.model.ts +++ b/src/models/quantumPortalTransaction.model.ts @@ -3,28 +3,29 @@ import mongoose, { Schema } from 'mongoose'; const quantumPortalTransactionSchema = new Schema({ hash: String, type: Number, - accessList: [Object], - networkId: String, - blockHash: String, + block: Number, + nonce: Number, + status: String, + method: String, blockNumber: Number, - transactionIndex: Number, confirmations: Number, from: String, + fromDetails: Object, gasPrice: String, + priorityFee: String, maxPriorityFeePerGas: String, + baseFeePerGas: String, maxFeePerGas: String, gasLimit: String, + gasUsed: String, to: String, + toDetails: Object, value: String, - valueToDisplay: String, - nonce: Number, - data: String, - r: String, - s: String, - v: Number, - creates: String, chainId: Number, - wait: String, + timestamp: String, + fee: String, + decodedInput: Object, + logs: [Object], }); export const QuantumPortalTransactionModel = mongoose.model( diff --git a/src/services/block.service.ts b/src/services/block.service.ts index 559f92c..2e60dae 100644 --- a/src/services/block.service.ts +++ b/src/services/block.service.ts @@ -4,6 +4,10 @@ import { } from '../models'; export const saveBlock = async (block: any) => { + const existedBlock = await getBlockByQuery({ number: block.number }); + if (existedBlock) { + return existedBlock; + } return await QuantumPortalBlockModel.create(block); }; diff --git a/src/services/node.service.ts b/src/services/node.service.ts index 1ea64ae..3912572 100644 --- a/src/services/node.service.ts +++ b/src/services/node.service.ts @@ -1,35 +1,79 @@ -import { ethers } from 'ethers'; +import axios from 'axios'; import * as blockService from './block.service'; import * as transactionsService from './transaction.service'; import config from '../config/config'; export const processBlockAndTransaction = async ( - startBlock: number = 0, + startBlock: number, endBlock: number, ): Promise => { console.log('node sync running', startBlock, endBlock); for (let blockNumber = startBlock; blockNumber <= endBlock; blockNumber++) { console.log(`Fetching block ${blockNumber}`); try { - const rpc = config.rpcUrl; - const provider = new ethers.providers.JsonRpcProvider(rpc); - // Fetch the block - const block = await provider.getBlockWithTransactions(blockNumber); - console.log({ block }); - if (block) { - await blockService.saveBlock(block); - await Promise.all( - block.transactions.map(async tx => { - console.log(` Transaction Hash: ${tx.hash}`); - return { - ...tx, - valueToDisplay: ethers.utils.formatEther(tx.value), - wait: await tx.wait(1), - }; - }), + const url = config.explorerUrl; + const block: any = await axios.get(`${url}/api/v2/blocks/${blockNumber}`); + + await blockService.saveBlock({ + hash: block.data.hash, + parentHash: block.data.parent_hash, + number: block.data.height, + nonce: block.data.nonce, + timestamp: block.data.timestamp, + difficulty: block.data.difficulty, + gasLimit: block.data.gas_limit, + gasUsed: block.data.gas_used, + miner: block.data.miner.hash, + baseFeePerGas: block.data.base_fee_per_gas, + txCount: block.data.tx_count, + rewards: block.data.rewards, + txsFees: block.data.tx_fees, + totalDifficulty: block.data.total_difficulty, + }); + console.log(`Block ${blockNumber} saved`); + console.log( + `Block ${blockNumber} has ${block?.data?.tx_count} transactions`, + ); + if (block?.data?.tx_count > 0) { + const txs = await axios.get( + `${url}/api/v2/blocks/${blockNumber}/transactions`, ); - console.log('block.transactions.size:', block.transactions?.length); - await transactionsService.saveTransactions(block.transactions); + txs?.data?.items?.forEach(async (tx: any) => { + console.log(`Fetching transaction ${tx.hash}`); + const logs = await axios.get( + `${url}/api/v2/transactions/${tx.hash}/logs`, + ); + console.log( + `Transaction ${tx.hash} has ${logs?.data?.items?.length} logs`, + ); + const saved = await transactionsService.saveTransaction({ + hash: tx.hash, + type: tx.type, + blockNumber: tx.block, + status: tx.status, + method: tx.method, + timestamp: tx.timestamp, + from: tx?.from?.hash, + fromDetails: tx?.from, + to: tx?.to?.hash, + toDetails: tx?.to, + block: tx?.block, + value: tx?.value, + fee: tx?.fee?.value, + gasLimit: tx?.gas_limit, + gasUsed: tx?.gas_used, + gasPrice: tx?.gas_price, + decodedInput: tx?.decoded_input, + logs: logs?.data?.items, + nonce: tx?.nonce, + confirmations: tx?.confirmations, + priorityFee: tx?.priority_fee, + maxPriorityFeePerGas: tx?.max_priority_fee_per_gas, + baseFeePerGas: tx?.base_fee_per_gas, + maxFeePerGas: tx?.max_fee_per_gas, + }); + console.log(`Transaction ${saved?.hash} saved`); + }); } else { console.log(`Block ${blockNumber} is empty`); } diff --git a/src/services/transaction.service.ts b/src/services/transaction.service.ts index ead8370..6297ccb 100644 --- a/src/services/transaction.service.ts +++ b/src/services/transaction.service.ts @@ -5,6 +5,19 @@ import MGRContractABI from '../utils/abi/ledgerMgr.json'; import { ethers } from 'ethers'; import { ContractAddresses } from '../utils/constants'; +export const saveTransaction = async (tx: any) => { + const existedTx = await QuantumPortalTransactionModel.findOne({ + hash: tx.hash, + }); + if (existedTx) { + console.log('Transaction already exists'); + return; + } + const transaction = new QuantumPortalTransactionModel(tx); + console.log('Saving transaction', transaction); + return await transaction.save(); +}; + export const getTxs = async ( page: number, limit: number, @@ -74,6 +87,7 @@ export const getAllTransactions = async ( }; export const saveTransactions = async (txs: any[]) => { + console.log({ txs }); return await QuantumPortalTransactionModel.insertMany(txs); };