@pgchain/blockchain-libs
Version:
PGWallet Blockchain Libs
270 lines • 11 kB
JavaScript
;
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