bitcore-node
Version:
A blockchain indexing node with extended capabilities using bitcore
279 lines • 11.9 kB
JavaScript
"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