@kaiachain/ethers-ext
Version:
ethers.js extension for kaia blockchain
150 lines (149 loc) • 6.55 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.pollTransactionInPool = exports.populateFeePayerAndSignatures = exports.populateChainId = exports.eip155sign = exports.populateGasPrice = exports.populateGasLimit = exports.populateNonce = exports.populateTo = exports.populateFrom = exports.populateFromSync = exports.getTransactionRequest = void 0;
const constants_1 = require("@ethersproject/constants");
const logger_1 = require("@ethersproject/logger");
const properties_1 = require("@ethersproject/properties");
const web_1 = require("@ethersproject/web");
const lodash_1 = __importDefault(require("lodash"));
const js_ext_core_1 = require("@kaiachain/js-ext-core");
const logger = new logger_1.Logger("@kaiachain/ethers-ext");
// Normalize transaction request in Object or RLP format
async function getTransactionRequest(transactionOrRLP) {
if (lodash_1.default.isString(transactionOrRLP)) {
return (0, js_ext_core_1.parseTransaction)(transactionOrRLP);
}
else {
return (0, properties_1.resolveProperties)(transactionOrRLP);
}
}
exports.getTransactionRequest = getTransactionRequest;
// Below populateX() methods are partial replacements to:
// - ethers.Signer.checkTransaction()
// - ethers.Signer.populateTransaction()
// - ethers.JsonRpcSigner.sendUncheckedTransaction()
// populateFromSync is a synchronous method so it can be used in checkTransaction().
function populateFromSync(tx, expectedFrom) {
// See @ethersproject/abstract-signer/src/index.ts:Signer.checkTransaction()
if (!tx.from || tx.from == "0x") {
tx.from = expectedFrom;
}
else {
tx.from = Promise.all([
Promise.resolve(tx.from),
expectedFrom,
]).then(([from, expectedFrom]) => {
if (from?.toLowerCase() != expectedFrom?.toLowerCase()) {
logger.throwArgumentError(`from address mismatch (wallet address=${expectedFrom}) (tx.from=${from})`, "transaction", tx);
}
return from;
});
}
}
exports.populateFromSync = populateFromSync;
async function populateFrom(tx, expectedFrom) {
populateFromSync(tx, expectedFrom);
tx.from = await tx.from;
}
exports.populateFrom = populateFrom;
async function populateTo(tx, provider) {
if (!tx.to || tx.to == "0x") {
tx.to = constants_1.AddressZero;
}
else {
const address = await provider.resolveName(tx.to);
if (address == null) {
logger.throwArgumentError("provided ENS name resolves to null", "tx.to", tx.to);
}
}
}
exports.populateTo = populateTo;
async function populateNonce(tx, provider, fromAddress) {
if (!tx.nonce) {
tx.nonce = await provider.getTransactionCount(fromAddress);
}
}
exports.populateNonce = populateNonce;
async function populateGasLimit(tx, provider) {
if (!tx.gasLimit) {
// Sometimes Klaytn node's eth_estimateGas may return insufficient amount.
// To avoid this, add buffer to the estimated gas.
// References:
// - ethers.js uses estimateGas result as-is.
// - Metamask multiplies by 1 or 1.5 depending on chainId
// (https://github.com/MetaMask/metamask-extension/blob/v11.3.0/ui/ducks/send/helpers.js#L126)
// TODO: To minimize buffer, add constant intrinsic gas overhead instead of multiplier.
try {
const bufferMultiplier = 2.5;
const gasLimit = await provider.estimateGas(tx);
tx.gasLimit = Math.ceil(gasLimit.toNumber() * bufferMultiplier);
}
catch (error) {
logger.throwError("cannot estimate gas; transaction may fail or may require manual gas limit", logger_1.Logger.errors.UNPREDICTABLE_GAS_LIMIT, {
error: error,
tx: tx
});
}
}
}
exports.populateGasLimit = populateGasLimit;
async function populateGasPrice(tx, provider) {
if (!tx.gasPrice) {
tx.gasPrice = await provider.getGasPrice();
}
}
exports.populateGasPrice = populateGasPrice;
function eip155sign(key, digest, chainId) {
const sig = key.signDigest(digest);
sig.v = sig.recoveryParam + chainId * 2 + 35;
return sig;
}
exports.eip155sign = eip155sign;
async function populateChainId(tx, provider) {
if (!tx.chainId) {
tx.chainId = ((0, js_ext_core_1.getChainIdFromSignatureTuples)(tx.txSignatures) ??
(0, js_ext_core_1.getChainIdFromSignatureTuples)(tx.feePayerSignatures) ??
(await provider.getNetwork()).chainId);
}
}
exports.populateChainId = populateChainId;
async function populateFeePayerAndSignatures(tx, expectedFeePayer) {
// A SenderTxHashRLP returned from caver may have dummy feePayer even if SenderTxHashRLP shouldn't have feePayer.
// So ignore AddressZero in the feePayer field.
if (!tx.feePayer || tx.feePayer == constants_1.AddressZero) {
tx.feePayer = expectedFeePayer;
}
else {
if (tx.feePayer.toLowerCase() != expectedFeePayer.toLowerCase()) {
logger.throwArgumentError("feePayer address mismatch", "transaction", tx);
}
}
// A SenderTxHashRLP returned from caver may have dummy feePayerSignatures if SenderTxHashRLP shouldn't have feePayerSignatures.
// So ignore [ '0x01', '0x', '0x' ] in the feePayerSignatures field.
if (lodash_1.default.isArray(tx.feePayerSignatures)) {
tx.feePayerSignatures = tx.feePayerSignatures.filter((sig) => {
return !(lodash_1.default.isArray(sig) && sig.length == 3 && sig[0] == "0x01" && sig[1] == "0x" && sig[2] == "0x");
});
}
}
exports.populateFeePayerAndSignatures = populateFeePayerAndSignatures;
// Poll for `eth_getTransaction` until the transaction is found in the transaction pool.
async function pollTransactionInPool(txhash, provider) {
// Retry until the transaction shows up in the txpool
// Using poll() like in the ethers.JsonRpcSigner.sendTransaction
// https://github.com/ethers-io/ethers.js/blob/v5.7/packages/providers/src.ts/json-rpc-provider.ts#L283
const pollFunc = async () => {
const tx = await provider.getTransaction(txhash);
if (tx == null) {
return undefined; // retry
}
else {
return tx; // success
}
};
return (0, web_1.poll)(pollFunc);
}
exports.pollTransactionInPool = pollTransactionInPool;