UNPKG

bitcore-node

Version:

A blockchain indexing node with extended capabilities using bitcore

279 lines 11.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EVMRouter = void 0; const cors_1 = __importDefault(require("cors")); const express_1 = require("express"); const web3_1 = __importDefault(require("web3")); const config_1 = __importDefault(require("../../../../config")); const logger_1 = __importDefault(require("../../../../logger")); const webhook_1 = require("../../../../models/webhook"); const config_2 = require("../../../../services/config"); const utils_1 = require("../../../../utils"); const opGasPriceOracle_1 = require("../abi/opGasPriceOracle"); const gnosis_1 = require("./gnosis"); class EVMRouter { constructor(csp, chain, params) { this.csp = csp; this.chain = chain?.toUpperCase(); this.router = (0, express_1.Router)(); this.router.param('network', (req, _res, next) => { const { network: beforeNetwork } = req.params; const { network } = config_2.Config.aliasFor({ chain: this.chain, network: beforeNetwork }); req.params.network = network; next(); }); this.setDefaultRoutes(this.router); if (params?.multisig) { this.setMultiSigRoutes(this.router); } this.setWebhooks(this.router); } ; getRouter() { return this.router; } ; setDefaultRoutes(router) { this.getAccountNonce(router); this.estimateGas(router); this.getTokenInfo(router); this.getERC20TokenAllowance(router); this.getPriorityFee(router); this.estimateL1Fee(router); } ; setMultiSigRoutes(router) { this.getMultisigEthInfo(router); this.getMultisigContractInstantiationInfo(router); this.getMultisigTxpsInfo(router); this.streamGnosisWalletTransactions(router); } ; setWebhooks(router) { this.postMoralisWebhook(router); } getAccountNonce(router) { router.get(`/api/${this.chain}/:network/address/:address/txs/count`, async (req, res) => { let { address, network } = req.params; try { const nonce = await this.csp.getAccountNonce(network, address); res.json({ nonce }); } catch (err) { logger_1.default.error('Nonce Error::%o', err.stack || err.message || err); res.status(500).send(err.message || err); } }); } ; estimateGas(router) { router.post(`/api/${this.chain}/:network/gas`, async (req, res) => { const { from, to, value, data } = req.body; const { network } = req.params; try { const gasLimit = await this.csp.estimateGas({ network, from, to, value, data }); res.json(gasLimit); } catch (err) { if (err?.code != null) { // Preventable error from geth (probably due to insufficient funds or similar) res.status(400).send(err.message); } else { logger_1.default.error('Gas Error::%o', err.stack || err.message || err); res.status(500).send(err.message || err); } } }); } ; estimateL1Fee(router) { router.post(`/api/${this.chain}/:network/l1/fee`, async (req, res) => { try { const { network } = req.params; const { rawTx } = req.body; const { safe } = req.query; const { needsL1Fee } = config_2.Config.chainConfig({ chain: this.chain, network }); if (!needsL1Fee) { return res.json('0'); // No L1 fee required } if (!rawTx) { return res.status(400).send('unsigned `rawTx` is required'); } // Reference: https://docs.optimism.io/builders/app-developers/transactions/estimates const packedRawTx = web3_1.default.utils.encodePacked(rawTx); const rawTxBuf = Buffer.from(packedRawTx.slice(2), 'hex'); const { web3 } = await this.csp.getWeb3(network); const gasPriceOracle = new web3.eth.Contract(opGasPriceOracle_1.OPGasPriceOracleAbi, opGasPriceOracle_1.OPGasPriceOracleAddress); let l1DataFee; if ((0, utils_1.castToBool)(safe)) { l1DataFee = await gasPriceOracle.methods.getL1FeeUpperBound(rawTxBuf.length).call(); } else { l1DataFee = await gasPriceOracle.methods.getL1Fee(rawTxBuf).call(); } return res.json(l1DataFee.toString()); // this is the total L1 fee in wei (i.e. L1 feeRate * gas). } catch (err) { logger_1.default.error('L1 Fee Error::%o', err.stack || err.message || err); return res.status(500).send(err.message || err); } }); } getTokenInfo(router) { router.get(`/api/${this.chain}/:network/token/:tokenAddress`, async (req, res) => { const { network, tokenAddress } = req.params; try { const tokenInfo = await this.csp.getERC20TokenInfo(network, tokenAddress); res.json(tokenInfo); } catch (err) { logger_1.default.error('Token Info Error::%o', err.stack || err.message || err); res.status(500).send(err.message || err); } }); } ; getERC20TokenAllowance(router) { router.get(`/api/${this.chain}/:network/token/:tokenAddress/allowance/:ownerAddress/for/:spenderAddress`, async (req, res) => { const { network, tokenAddress, ownerAddress, spenderAddress } = req.params; try { const allowance = await this.csp.getERC20TokenAllowance(network, tokenAddress, ownerAddress, spenderAddress); res.json(allowance); } catch (err) { logger_1.default.error('Token Allowance Error::%o', err.stack || err.message || err); res.status(500).send(err.message || err); } }); } ; getPriorityFee(router) { router.get(`/api/${this.chain}/:network/priorityFee/:percentile`, async (req, res) => { let { percentile, network } = req.params; const priorityFeePercentile = Number(percentile) || 15; network = network.toLowerCase(); try { let fee = await this.csp.getPriorityFee({ network, percentile: priorityFeePercentile }); if (!fee) { return res.status(404).send('not available right now'); } return res.json(fee); } catch (err) { logger_1.default.error('Fee Error: %o', err.stack || err.message || err); return res.status(500).send('Error getting priority fee from RPC'); } }); } ; streamGnosisWalletTransactions(router) { router.get(`/api/${this.chain}/:network/ethmultisig/transactions/:multisigContractAddress`, async (req, res) => { let { network, multisigContractAddress } = req.params; try { return await gnosis_1.Gnosis.streamGnosisWalletTransactions({ chain: this.chain, network, multisigContractAddress, wallet: {}, req, res, args: req.query }); } catch (err) { logger_1.default.error('Multisig Transactions Error::%o', err.stack || err.message || err); return res.status(500).send(err.message || err); } }); } ; getMultisigTxpsInfo(router) { router.get(`/api/${this.chain}/:network/ethmultisig/txps/:multisigContractAddress`, async (req, res) => { const { network, multisigContractAddress } = req.params; try { const multisigTxpsInfo = await gnosis_1.Gnosis.getMultisigTxpsInfo(this.chain, network, multisigContractAddress); res.json(multisigTxpsInfo); } catch (err) { logger_1.default.error('Multisig Txps Error::%o', err.stack || err.message || err); res.status(500).send(err.message || err); } }); } ; getMultisigContractInstantiationInfo(router) { router.get(`/api/${this.chain}/:network/ethmultisig/:sender/instantiation/:txId`, async (req, res) => { const { network, sender, txId } = req.params; try { const multisigInstantiationInfo = await gnosis_1.Gnosis.getMultisigContractInstantiationInfo(this.chain, network, sender, txId); res.json(multisigInstantiationInfo); } catch (err) { logger_1.default.error('Multisig Instantiation Error::%o', err.stack || err.message || err); res.status(500).send(err.message || err); } }); } ; getMultisigEthInfo(router) { router.get(`/api/${this.chain}/:network/ethmultisig/info/:multisigContractAddress`, async (req, res) => { const { network, multisigContractAddress } = req.params; try { const multisigInfo = await gnosis_1.Gnosis.getMultisigInfo(this.chain, network, multisigContractAddress); res.json(multisigInfo); } catch (err) { logger_1.default.error('Multisig Info Error::%o', err.stack || err.message || err); res.status(500).send(err.message || err); } }); } ; _validateMoralisWebhook(req, res, next) { const secret = config_1.default.externalProviders?.moralis?.streamSecret; if (!secret) { return res.status(404).send('Moralis not configured'); } const reqSig = req.headers['x-signature']; if (!reqSig) { return res.status(400).send('Signature not provided'); } const computedSig = web3_1.default.utils.sha3(JSON.stringify(req.body) + secret); if (reqSig !== computedSig) { return res.status(406).send('Unauthorized'); } next(); } postMoralisWebhook(router) { const webhookCors = config_1.default.externalProviders?.moralis?.webhookCors; router.post(`/webhook/${this.chain}/:network/moralis`, (0, cors_1.default)(webhookCors), this._validateMoralisWebhook, async (req, res) => { try { const { network } = req.params; if (req.body.chainId === '') { // This is a webhook test call from moralis return res.end(); } await webhook_1.WebhookStorage.collection.insertOne({ chain: this.chain, network, source: 'moralis', sourceId: req.body.streamId, tag: req.body.tag, body: req.body, timestamp: new Date(), processed: false }); return res.end(); } catch (err) { logger_1.default.error('Error processing moralis webhook: %o', err.stack || err.message || err); return res.status(500).send('Unable to process webhook'); } }); } } exports.EVMRouter = EVMRouter; //# sourceMappingURL=routes.js.map