UNPKG

@pgchain/blockchain-libs

Version:
270 lines 11 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Geth = void 0; const abi_1 = require("@ethersproject/abi"); const bignumber_js_1 = __importDefault(require("bignumber.js")); const bignumber_plus_1 = require("../../../basic/bignumber-plus"); const precondtion_1 = require("../../../basic/precondtion"); const json_rpc_1 = require("../../../basic/request/json-rpc"); const provider_1 = require("../../../types/provider"); const abc_1 = require("../../abc"); const mm_fee_1 = require("./mm-fee"); function decodeStringCallResult(hexResult) { if (hexResult.length <= 2) { throw new Error('Invalid hex result.'); } try { return abi_1.defaultAbiCoder.decode(['string'], hexResult)[0]; } catch (e) { console.error(e); // Non-standard name or symbol, type of bytes32. // See 0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2 return Buffer.from(hexResult.slice(2), 'hex') .toString() .replace(/\0+$/, ''); } } class Geth extends abc_1.BaseClient { constructor(url) { super(); this.rpc = new json_rpc_1.JsonRPCRequest(url); } get mmFee() { var _a, _b; if (!this._mmFee) { this._mmFee = new mm_fee_1.MmFee(Number((_b = (_a = this.chainInfo) === null || _a === void 0 ? void 0 : _a.implOptions) === null || _b === void 0 ? void 0 : _b.chainId)); } return this._mmFee; } async getInfo() { const latestBlock = await this.rpc.call('eth_getBlockByNumber', [ Geth.__LAST_BLOCK__, false, ]); const bestBlockNumber = parseInt(latestBlock.number, 16); const isReady = !isNaN(bestBlockNumber) && bestBlockNumber > 0; return { bestBlockNumber, isReady }; } async getAddresses(addresses) { const calls = addresses.reduce((acc, cur) => { acc.push(['eth_getBalance', [cur, Geth.__LAST_BLOCK__]]); acc.push(['eth_getTransactionCount', [cur, Geth.__LAST_BLOCK__]]); return acc; }, []); const resp = await this.rpc.batchCall(calls, undefined, undefined, true); const result = []; for (let i = 0, count = resp.length; i < count; i += 2) { const [balanceHex, nonceHex] = resp.slice(i, i + 2); let info = undefined; if (typeof balanceHex !== 'undefined' && typeof nonceHex !== 'undefined') { const balance = (0, bignumber_plus_1.fromBigIntHex)(balanceHex); const nonce = parseInt(nonceHex, 16); if (!balance.isNaN() && !isNaN(nonce)) { info = { balance, nonce, existing: balance.gt(0) || nonce > 0, }; } } result.push(info); } return result; } async getBalances(requests) { const calls = requests.map((i) => { var _a; return ((_a = i.coin) === null || _a === void 0 ? void 0 : _a.tokenAddress) ? [ 'eth_call', [ { to: i.coin.tokenAddress, data: '0x70a08231000000000000000000000000' + i.address.toLowerCase().slice(2), }, Geth.__LAST_BLOCK__, ], ] : ['eth_getBalance', [i.address, Geth.__LAST_BLOCK__]]; }); const resp = await this.rpc.batchCall(calls, undefined, undefined, true); return resp.map((i) => { let balance = undefined; if (typeof i !== 'undefined') { balance = (0, bignumber_plus_1.fromBigIntHex)(i.slice(0, 66)); if (balance.isNaN()) { balance = undefined; } } return balance; }); } async getTransactionStatuses(txids) { const calls = txids.reduce((acc, cur) => { acc.push(['eth_getTransactionByHash', [cur]]); acc.push(['eth_getTransactionReceipt', [cur]]); return acc; }, []); const resp = await this.rpc.batchCall(calls, undefined, undefined, true); const result = []; for (let i = 0, count = resp.length; i < count; i += 2) { const [tx, receipt] = resp.slice(i, i + 2); let status = undefined; if (typeof tx !== 'undefined' && typeof receipt !== 'undefined') { if (!tx) { status = provider_1.TransactionStatus.NOT_FOUND; } else if (!receipt) { status = provider_1.TransactionStatus.PENDING; } else { status = receipt.status === '0x1' ? provider_1.TransactionStatus.CONFIRM_AND_SUCCESS : provider_1.TransactionStatus.CONFIRM_BUT_FAILED; } } result.push(status); } return result; } async getTokenInfos(tokenAddresses) { const data = ['0x95d89b41', '0x06fdde03', '0x313ce567']; // method_selector of symbol, name and decimals const calls = tokenAddresses.reduce((acc, cur) => { const item = data .map((i) => ({ to: cur, data: i })) .map((i) => ['eth_call', [i, Geth.__LAST_BLOCK__]]); acc.push(...item); return acc; }, []); const resp = await this.rpc.batchCall(calls, undefined, undefined, true); const tokens = []; for (let i = 0, count = resp.length; i < count; i += 3) { const [symbolHex, nameHex, decimalsHex] = resp.slice(i, i + 3); if (typeof symbolHex === 'undefined' || typeof nameHex === 'undefined' || typeof decimalsHex === 'undefined') { tokens.push(undefined); continue; } try { const symbol = decodeStringCallResult(symbolHex); const name = decodeStringCallResult(nameHex); const decimals = parseInt(decimalsHex, 16); (0, precondtion_1.check)(!isNaN(decimals)); tokens.push({ symbol, name, decimals, }); } catch (e) { console.error(e); tokens.push(undefined); } } return tokens; } async getFeePricePerUnit() { var _a, _b, _c; if (((_b = (_a = this.chainInfo) === null || _a === void 0 ? void 0 : _a.implOptions) === null || _b === void 0 ? void 0 : _b.EIP1559Enabled) === true) { const eip1559Fee = await this.estimateEIP1559FeeStrategy(); const toLegacy = (price) => ({ price: eip1559Fee.baseFee.plus(price.maxPriorityFeePerGas), payload: price, }); return { normal: toLegacy(eip1559Fee.normal), others: (_c = eip1559Fee.others) === null || _c === void 0 ? void 0 : _c.map(toLegacy), }; } const gasPriceHex = await this.rpc.call('eth_gasPrice', []); const gasPrice = (0, bignumber_plus_1.fromBigIntHex)(gasPriceHex); const slow = gasPrice.isFinite() && gasPrice.gt(1) ? gasPrice : new bignumber_js_1.default(1); const normal = slow.multipliedBy(1.25).integerValue(bignumber_js_1.default.ROUND_CEIL); const fast = normal.multipliedBy(1.2).integerValue(bignumber_js_1.default.ROUND_CEIL); // 1.25 * 1.2 = 1.5 return { normal: { price: normal }, others: [{ price: slow }, { price: fast }], }; } async estimateEIP1559FeeStrategy() { var _a, _b, _c, _d; try { if (((_b = (_a = this.chainInfo) === null || _a === void 0 ? void 0 : _a.implOptions) === null || _b === void 0 ? void 0 : _b.EIP1559Enabled) === true && ((_d = (_c = this.chainInfo) === null || _c === void 0 ? void 0 : _c.implOptions) === null || _d === void 0 ? void 0 : _d.preferMetamask) === true) { return this.mmFee.estimateEIP1559Fee(); } } catch (e) { console.error('Failed to estimate EIP1559 fee for MM', e); } return this.estimateEIP1559Fee(); } async estimateEIP1559Fee() { const [latestBlock, feeHistory] = await this.rpc.batchCall([ ['eth_getBlockByNumber', ['pending', false]], ['eth_feeHistory', [20, 'pending', [5, 25, 80]]], ]); const baseFee = new bignumber_js_1.default(latestBlock.baseFeePerGas); const avg = (nums) => { nums = nums.filter((i) => i > 0); return new bignumber_js_1.default(Math.round(nums.reduce((a, c) => a + c) / nums.length)); }; const slow = avg(feeHistory.reward.map((i) => Number(i[0]))); const normal = avg(feeHistory.reward.map((i) => Number(i[1]))); const fast = avg(feeHistory.reward.map((i) => Number(i[2]))); return { baseFee: baseFee, normal: { maxPriorityFeePerGas: normal, maxFeePerGas: baseFee.multipliedBy(1.25).plus(normal).integerValue(), }, others: [ { maxPriorityFeePerGas: slow, maxFeePerGas: baseFee.multipliedBy(1.13).plus(slow).integerValue(), }, { maxPriorityFeePerGas: fast, maxFeePerGas: baseFee.multipliedBy(1.3).plus(fast).integerValue(), }, ], }; } async broadcastTransaction(rawTx) { return await this.rpc.call('eth_sendRawTransaction', [rawTx]); } estimateGasLimit(fromAddress, toAddress, value, data) { return this.rpc.call('eth_estimateGas', [ { from: fromAddress, to: toAddress || undefined, value: value, data: data || '0x', }, ]); } async isContract(address) { let code = await this.rpc.call('eth_getCode', [ address, Geth.__LAST_BLOCK__, ]); if (code && code.startsWith('0x')) { code = code.slice(2); } return code.length > 0; } async batchEthCall(calls) { return this.rpc.batchCall(calls.map((i) => ['eth_call', [i, Geth.__LAST_BLOCK__]])); } } exports.Geth = Geth; Geth.__LAST_BLOCK__ = 'latest'; //# sourceMappingURL=geth.js.map