jcc-ethereum-utils
Version:
Toolkit of crossing chain from Ethereum to SWTC chain
512 lines • 16.6 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const jcc_wallet_1 = require("jcc_wallet");
const web3_eth_contract_1 = require("web3-eth-contract");
const bignumber_js_1 = require("bignumber.js");
const { Web3 } = require("web3");
/**
* Toolkit of Ethereum
*
* @export
* @class Ethereum
*/
class Ethereum {
/**
* Creates an instance of Ethereum
* @param {string} node http node
* @memberof Ethereum
*/
constructor(node) {
this._web3 = null;
this._node = node;
this._gasLimit = 200000;
this._minGasPrice = 5 * Math.pow(10, 9);
this._defaultGasPrice = Math.pow(10, 10);
this._minFeePerGas = 5 * Math.pow(10, 9);
this._minPriorityFeePerGas = Math.pow(10, 9);
}
/**
* set & get _gasLimit
*
* @type {number}
* @memberof Ethereum
*/
get gasLimit() {
return this._gasLimit;
}
set gasLimit(gas) {
this._gasLimit = gas;
}
/**
* set & get _minGasPrice
*
* @type {number}
* @memberof Ethereum
*/
get minGasPrice() {
return this._minGasPrice;
}
set minGasPrice(value) {
this._minGasPrice = value;
}
/**
* set & get _defaultGasPrice
*
* @memberof Ethereum
*/
set defaultGasPrice(v) {
this._defaultGasPrice = v;
}
get defaultGasPrice() {
return this._defaultGasPrice;
}
/**
* set & get _minFeePerGas
*
* @type {number}
* @memberof Ethereum
*/
set minFeePerGas(v) {
this._minFeePerGas = v;
}
get minFeePerGas() {
return this._minFeePerGas;
}
/**
* set & get _minPriorityFeePerGas
*
* @type {number}
* @memberof Ethereum
*/
set minPriorityFeePerGas(v) {
this._minPriorityFeePerGas = v;
}
get minPriorityFeePerGas() {
return this._minPriorityFeePerGas;
}
/**
* validate ethereum address
*
* @static
* @param {string} address ethereum address
* @returns {boolean} return true if the address is valid
* @memberof Ethereum
*/
static isValidAddress(address) {
return jcc_wallet_1.ethWallet.isValidAddress(address);
}
/**
* validate ethereum secret
*
* @static
* @param {string} secret ethereum secret
* @returns {boolean} return true if the secret is valid
* @memberof Ethereum
*/
static isValidSecret(secret) {
return jcc_wallet_1.ethWallet.isValidSecret(secret);
}
/**
* retrieve ethereum address via secret
*
* @static
* @param {string} secret ethereum secret
* @returns {string} return address if the secret is valid, otherwise return null
* @memberof Ethereum
*/
static getAddress(secret) {
return jcc_wallet_1.ethWallet.getAddress(secret);
}
/**
* create ethereum wallet
*
* @static
* @returns {IWalletModel}
* @memberof Ethereum
*/
static createWallet() {
return jcc_wallet_1.ethWallet.createWallet();
}
/**
* prefix `0x` if the given string not start with `0x`
*
* @static
* @param {string} str
* @returns {string}
* @memberof Ethereum
*/
static prefix0x(str) {
if (str && !str.startsWith("0x")) {
str = "0x" + str;
}
return str;
}
/**
* filter `0x` if the given string starts with `0x`
*
* @static
* @param {string} str
* @returns {string}
* @memberof Ethereum
*/
static filter0x(str) {
if (typeof str !== "string") {
return str;
}
return str.startsWith("0x") ? str.substring(2) : str;
}
/**
* init instance of web3
*
* @memberof Ethereum
*/
initWeb3() {
if (!this._web3 || !this._web3.currentProvider) {
// Only allow safe transport schemes to prevent protocol-injection attacks.
const allowedSchemes = ["http:", "https:", "ws:", "wss:"];
let parsedUrl;
try {
parsedUrl = new URL(this._node);
}
catch (_a) {
throw new Error(`Invalid node URL: ${this._node}`);
}
if (!allowedSchemes.includes(parsedUrl.protocol)) {
throw new Error(`Unsupported URL scheme "${parsedUrl.protocol}". Allowed: http, https, ws, wss.`);
}
this._web3 = new Web3(new Web3.providers.HttpProvider(this._node));
}
}
/**
* destroy instance of web3
*
* @memberof Ethereum
*/
destroyWeb3() {
try {
this._web3.setProvider(null);
}
catch (error) {
/* istanbul ignore next */
}
finally {
this._web3 = null;
}
}
/**
* get instance of web3
*
* @memberof Ethereum
*/
getWeb3() {
return this._web3;
}
/**
* request info of block
*
* @param {number|string} block number or string latest
* @returns {Promise<any>} resolve null if request failed, return block info
* @memberof Ethereum
*/
getBlock(block) {
return __awaiter(this, void 0, void 0, function* () {
let blockInfo;
try {
blockInfo = yield this._web3.eth.getBlock(block);
}
catch (error) {
blockInfo = null;
}
return blockInfo;
});
}
/**
* request balance of ether
*
* @param {string} address ethereum address
* @returns {Promise<string>} resolve "0" if request failed
* @memberof Ethereum
*/
getBalance(address) {
return __awaiter(this, void 0, void 0, function* () {
const wei = yield this._web3.eth.getBalance(address);
const balance = this._web3.utils.fromWei(wei, "ether");
return balance;
});
}
/**
* request current gas price
*
* @returns {Promise<number>} resolve gas price if success
* @memberof Ethereum
*/
getGasPrice() {
return __awaiter(this, void 0, void 0, function* () {
try {
const gasPrice = yield this._web3.eth.getGasPrice();
const bn = new bignumber_js_1.BigNumber(gasPrice);
return bn.lte(this._minGasPrice) ? this._minGasPrice : bn.toNumber();
}
catch (error) {
return this._defaultGasPrice;
}
});
}
/**
* request current fee data
*
* @returns {Promise<IFeeData>} resolve gas price if success
* @memberof Ethereum
*/
getFeeData() {
return __awaiter(this, void 0, void 0, function* () {
try {
const feeData = yield this._web3.eth.calculateFeeData();
const gasPrice = new bignumber_js_1.BigNumber(feeData.gasPrice);
feeData.gasPrice = gasPrice.lte(this._minGasPrice) ? this._minGasPrice : gasPrice.toNumber();
if (feeData.baseFeePerGas) {
const maxFeePerGas = new bignumber_js_1.BigNumber(feeData.maxFeePerGas);
const maxPriorityFeePerGas = new bignumber_js_1.BigNumber(feeData.maxPriorityFeePerGas);
feeData.baseFeePerGas = new bignumber_js_1.BigNumber(feeData.baseFeePerGas).toNumber();
feeData.maxFeePerGas = maxFeePerGas.lte(this._minGasPrice) ? this._minGasPrice : maxFeePerGas.toNumber();
feeData.maxPriorityFeePerGas = maxPriorityFeePerGas.lte(this._minPriorityFeePerGas) ? this._minPriorityFeePerGas : maxPriorityFeePerGas.toNumber();
}
return feeData;
}
catch (error) {
return { gasPrice: this._defaultGasPrice };
}
});
}
/**
* request nonce
*
* @param {string} address ethereum address
* @returns {Promise<number>} resolve nonce if success
* @memberof Ethereum
*/
getNonce(address) {
return __awaiter(this, void 0, void 0, function* () {
address = Ethereum.prefix0x(address);
try {
const ethCount = yield this._web3.eth.getTransactionCount(address);
return new bignumber_js_1.BigNumber(ethCount).toNumber();
}
catch (error) {
throw error;
}
});
}
/**
* check if has pending transaction
*
* @param {string} address
* @returns {Promise<boolean>} resolve true if has pending transaction
* @memberof Ethereum
*/
hasPendingTransactions(address) {
return __awaiter(this, void 0, void 0, function* () {
try {
const pendingList = yield this._web3.eth.getPendingTransactions();
address = Ethereum.prefix0x(address).toLowerCase();
let hasPending = false;
for (const pending of pendingList) {
if (address.includes(pending.from.toLowerCase())) {
hasPending = true;
break;
}
}
return hasPending;
}
catch (error) {
throw error;
}
});
}
/**
* check if has pending block transaction
*
* @param {string} address
* @returns {Promise<boolean>} resolve true if has pending block transaction
* @memberof Ethereum
*/
hasPendingBlockTransactions(address) {
return __awaiter(this, void 0, void 0, function* () {
try {
const pendingBlock = yield this._web3.eth.getBlock("pending", true);
address = Ethereum.prefix0x(address).toLowerCase();
let hasPending = false;
for (const pending of pendingBlock.transactions) {
if (address.includes(pending.from.toLowerCase())) {
hasPending = true;
break;
}
}
return hasPending;
}
catch (error) {
throw error;
}
});
}
/**
* format transaction info
*
* @param {string} from sender address
* @param {string} to destination address
* @param {number} nonce nonce
* @param {number} gasLimit gas limit
* @param {number} gasPrice gas price
* @param {string} value value
* @param {string} calldata call data
* @returns {EthereumTransaction}
* @memberof Ethereum
*/
getTx(from, to, nonce, gasLimit, gasPrice, value, calldata) {
const tx = {
data: !calldata ? "0x0" : calldata,
from,
gas: this._web3.utils.numberToHex(gasLimit),
gasPrice: this._web3.utils.numberToHex(gasPrice),
nonce,
to,
value: (value === null || value === void 0 ? void 0 : value.startsWith("0x")) ? value : this._web3.utils.numberToHex(this._web3.utils.toWei(value + "", "ether"))
};
// 在判断uint类型时,toHex条件判断似乎有问题,所以这里使用numberToHex
return tx;
}
/**
* format EIP1559 transaction info
*
* @param {string} from sender address
* @param {string} to destination address
* @param {number} nonce nonce
* @param {number} gasLimit gas limit
* @param {number} maxFeePerGas max fee per gas
* @param {number} maxPriorityFeePerGas max priority fee per gas
* @param {string} value value
* @param {string} calldata call data
* @returns {EthereumTransaction}
* @memberof Ethereum
*/
get1559Tx(from, to, nonce, gasLimit, maxFeePerGas, maxPriorityFeePerGas, value, calldata) {
const tx = {
from,
to,
nonce,
value: (value === null || value === void 0 ? void 0 : value.startsWith("0x")) ? value : this._web3.utils.numberToHex(this._web3.utils.toWei(value + "", "ether")),
gasLimit: this._web3.utils.numberToHex(gasLimit),
maxFeePerGas: this._web3.utils.numberToHex(maxFeePerGas),
maxPriorityFeePerGas: this._web3.utils.numberToHex(maxPriorityFeePerGas),
data: !calldata ? "0x0" : calldata
};
return tx;
}
/**
* sign transaction with ethereum secret
*
* @param {EthereumTransaction} tx transaction
* @param {string} secret ethereum secret
* @returns {Promise<string>} return signed info
* @memberof Ethereum
*/
signTransaction(tx, secret) {
return __awaiter(this, void 0, void 0, function* () {
const signed = yield this._web3.eth.accounts.signTransaction(tx, secret);
return signed.rawTransaction;
});
}
/**
* send signed transaction
*
* @param {string} sign
* @returns {Promise<string>} resolve hash if success
* @memberof Ethereum
*/
sendSignedTransaction(sign) {
return __awaiter(this, void 0, void 0, function* () {
// sendSignedTransaction会自动绑定receipt等事件,在销毁实例时会抛出错误
try {
const txInfo = yield this._web3.eth.sendSignedTransaction(sign);
// Explicitly check on-chain execution status. A falsy status means the
// transaction was mined but reverted, which must be surfaced to callers.
if (txInfo.status === false || txInfo.status === BigInt(0)) {
throw new Error(`Transaction reverted on-chain: ${txInfo.transactionHash}`);
}
return txInfo.transactionHash;
}
catch (error) {
throw error;
}
});
}
/**
* get transaction
*
* @param {string} hash transaction hash
* @returns {any} null or transaction object
* @memberof Ethereum
*/
getTransaction(hash) {
return __awaiter(this, void 0, void 0, function* () {
try {
const data = yield this._web3.eth.getTransaction(hash);
return data;
}
catch (error) {
throw error;
}
});
}
/**
* get transaction receipt
*
* @param {string} hash transaction hash
* @returns {any} null or transaction receipt object
* @memberof Ethereum
*/
getTransactionReceipt(hash) {
return __awaiter(this, void 0, void 0, function* () {
try {
const data = yield this._web3.eth.getTransactionReceipt(hash);
return data;
}
catch (error) {
throw error;
}
});
}
/**
* init instance of ethereum or erc20 contract
*
* @param {abitItem} abi definition of ethereum abi or erc20 abi
* @param {string} address
* @returns {Contract} return instance of ethereum or erc20 contract
* @memberof Ethereum
*/
contract(abi, address) {
return new this._web3.eth.Contract(abi, address);
}
/**
* check instance of contract if initialied
*
* @param {Contract} contract current contract instance
* @param {string} address current contract address
* @returns {boolean} return true if initialied
* @memberof Ethereum
*/
contractInitialied(contract, address) {
return contract instanceof web3_eth_contract_1.Contract && !!contract.options.address && contract.options.address.toLowerCase() === address.toLowerCase();
}
}
exports.default = Ethereum;
//# sourceMappingURL=ethereum.js.map