Skip to content

Commit a05dd50

Browse files
feat(abstract-eth): fetch gasPrice and gasLimit from explorer
2 parents 7bb4f8f + 2a8f36e commit a05dd50

File tree

2 files changed

+118
-31
lines changed

2 files changed

+118
-31
lines changed

modules/abstract-eth/src/abstractEthLikeNewCoins.ts

Lines changed: 117 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1520,25 +1520,20 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
15201520
}
15211521
}
15221522

1523-
const gasLimit = new optionalDeps.ethUtil.BN(this.setGasLimit(params.gasLimit));
1523+
// Use default gasLimit for cold and custody wallets
1524+
let gasLimit =
1525+
params.gasLimit || userKey.startsWith('xpub') || !userKey
1526+
? new optionalDeps.ethUtil.BN(this.setGasLimit(params.gasLimit))
1527+
: new optionalDeps.ethUtil.BN(0);
1528+
15241529
const gasPrice = params.eip1559
15251530
? new optionalDeps.ethUtil.BN(params.eip1559.maxFeePerGas)
1526-
: new optionalDeps.ethUtil.BN(this.setGasPrice(params.gasPrice));
1531+
: params.gasPrice
1532+
? new optionalDeps.ethUtil.BN(this.setGasPrice(params.gasPrice))
1533+
: await this.getGasPriceFromExternalAPI();
15271534

15281535
const bitgoFeeAddressNonce = await this.getAddressNonce(bitgoFeeAddress);
15291536

1530-
// get balance of bitgoFeeAddress to ensure funds are available to pay fees
1531-
const bitgoFeeAddressBalance = await this.queryAddressBalance(bitgoFeeAddress);
1532-
const totalGasNeeded = gasPrice.mul(gasLimit);
1533-
const weiToGwei = 10 ** 9;
1534-
if (bitgoFeeAddressBalance.lt(totalGasNeeded)) {
1535-
throw new Error(
1536-
`Fee address ${bitgoFeeAddress} has balance ${(bitgoFeeAddressBalance / weiToGwei).toString()} Gwei.` +
1537-
`This address must have a balance of at least ${(totalGasNeeded / weiToGwei).toString()}` +
1538-
` Gwei to perform recoveries. Try sending some ${this.getChain()} to this address then retry.`
1539-
);
1540-
}
1541-
15421537
if (tokenContractAddress) {
15431538
return this.recoverEthLikeTokenforEvmBasedRecovery(
15441539
params,
@@ -1588,14 +1583,6 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
15881583
await new Promise((resolve) => setTimeout(resolve, 1000));
15891584
const sequenceId = await this.querySequenceId(walletContractAddress);
15901585

1591-
const txInfo = {
1592-
recipients: recipients,
1593-
expireTime: this.getDefaultExpireTime(),
1594-
contractSequenceId: sequenceId,
1595-
gasLimit: gasLimit.toString(10),
1596-
isEvmBasedCrossChainRecovery: true,
1597-
};
1598-
15991586
const network = this.getNetwork();
16001587
const batcherContractAddress = network?.batcherContractAddress as string;
16011588

@@ -1646,8 +1633,33 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
16461633
txBuilder.walletVersion(4);
16471634
}
16481635

1636+
// If gasLimit was not passed as a param, then fetch the gasLimit from Explorer
1637+
if (!params.gasLimit && !userKey.startsWith('xpub')) {
1638+
const sendData = txBuilder.getSendData();
1639+
gasLimit = await this.getGasLimitFromExternalAPI(
1640+
params.bitgoFeeAddress as string,
1641+
params.walletContractAddress,
1642+
sendData
1643+
);
1644+
txBuilder.fee({
1645+
...txFee,
1646+
gasLimit: gasLimit.toString(),
1647+
});
1648+
}
1649+
1650+
// Get the balance of bitgoFeeAddress to ensure funds are available to pay fees
1651+
await this.ensureSufficientBalance(bitgoFeeAddress, gasPrice, gasLimit);
1652+
16491653
const tx = await txBuilder.build();
16501654

1655+
const txInfo = {
1656+
recipients: recipients,
1657+
expireTime: this.getDefaultExpireTime(),
1658+
contractSequenceId: sequenceId,
1659+
gasLimit: gasLimit.toString(10),
1660+
isEvmBasedCrossChainRecovery: true,
1661+
};
1662+
16511663
const response: OfflineVaultTxInfo = {
16521664
txHex: tx.toBroadcastFormat(),
16531665
userKey,
@@ -1740,14 +1752,6 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
17401752
await new Promise((resolve) => setTimeout(resolve, 1000));
17411753
const sequenceId = await this.querySequenceId(params.walletContractAddress);
17421754

1743-
const txInfo = {
1744-
recipients: recipients,
1745-
expireTime: this.getDefaultExpireTime(),
1746-
contractSequenceId: sequenceId,
1747-
gasLimit: gasLimit.toString(10),
1748-
isEvmBasedCrossChainRecovery: true,
1749-
};
1750-
17511755
const txBuilder = this.getTransactionBuilder(params.common) as TransactionBuilder;
17521756
txBuilder.counter(bitgoFeeAddressNonce);
17531757
txBuilder.contract(params.walletContractAddress as string);
@@ -1799,8 +1803,32 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
17991803
txBuilder.walletVersion(4);
18001804
}
18011805

1806+
if (!params.gasLimit && !userKey.startsWith('xpub')) {
1807+
const sendData = txBuilder.getSendData();
1808+
gasLimit = await this.getGasLimitFromExternalAPI(
1809+
params.bitgoFeeAddress as string,
1810+
params.walletContractAddress,
1811+
sendData
1812+
);
1813+
txBuilder.fee({
1814+
...txFee,
1815+
gasLimit: gasLimit.toString(),
1816+
});
1817+
}
1818+
1819+
// Get the balance of bitgoFeeAddress to ensure funds are available to pay fees
1820+
await this.ensureSufficientBalance(params.bitgoFeeAddress as string, gasPrice, gasLimit);
1821+
18021822
const tx = await txBuilder.build();
18031823

1824+
const txInfo = {
1825+
recipients: recipients,
1826+
expireTime: this.getDefaultExpireTime(),
1827+
contractSequenceId: sequenceId,
1828+
gasLimit: gasLimit.toString(10),
1829+
isEvmBasedCrossChainRecovery: true,
1830+
};
1831+
18041832
const response: OfflineVaultTxInfo = {
18051833
txHex: tx.toBroadcastFormat(),
18061834
userKey,
@@ -2599,4 +2627,63 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
25992627
}
26002628
}
26012629
}
2630+
2631+
/**
2632+
* Fetch the gas price from the explorer
2633+
*/
2634+
async getGasPriceFromExternalAPI(): Promise<BN> {
2635+
try {
2636+
const res = await this.recoveryBlockchainExplorerQuery({
2637+
module: 'proxy',
2638+
action: 'eth_gasPrice',
2639+
});
2640+
const gasPrice = new BN(res.result.slice(2), 16);
2641+
console.log(` Got gas price: ${gasPrice}`);
2642+
return gasPrice;
2643+
} catch (e) {
2644+
throw new Error('Failed to get gas price');
2645+
}
2646+
}
2647+
2648+
/**
2649+
* Fetch the gas limit from the explorer
2650+
* @param from
2651+
* @param to
2652+
* @param data
2653+
*/
2654+
async getGasLimitFromExternalAPI(from: string, to: string, data: string): Promise<BN> {
2655+
try {
2656+
const res = await this.recoveryBlockchainExplorerQuery({
2657+
module: 'proxy',
2658+
action: 'eth_estimateGas',
2659+
from,
2660+
to,
2661+
data,
2662+
});
2663+
const gasLimit = new BN(res.result.slice(2), 16);
2664+
console.log(`Got gas limit: ${gasLimit}`);
2665+
return gasLimit;
2666+
} catch (e) {
2667+
throw new Error('Failed to get gas limit: ');
2668+
}
2669+
}
2670+
2671+
/**
2672+
* Get the balance of bitgoFeeAddress to ensure funds are available to pay fees
2673+
* @param bitgoFeeAddress
2674+
* @param gasPrice
2675+
* @param gasLimit
2676+
*/
2677+
async ensureSufficientBalance(bitgoFeeAddress: string, gasPrice: BN, gasLimit: BN): Promise<void> {
2678+
const bitgoFeeAddressBalance = await this.queryAddressBalance(bitgoFeeAddress);
2679+
const totalGasNeeded = Number(gasPrice.mul(gasLimit));
2680+
const weiToGwei = 10 ** 9;
2681+
if (bitgoFeeAddressBalance.lt(totalGasNeeded)) {
2682+
throw new Error(
2683+
`Fee address ${bitgoFeeAddress} has balance ${(bitgoFeeAddressBalance / weiToGwei).toString()} Gwei.` +
2684+
`This address must have a balance of at least ${(totalGasNeeded / weiToGwei).toString()}` +
2685+
` Gwei to perform recoveries. Try sending some ${this.getChain()} to this address then retry.`
2686+
);
2687+
}
2688+
}
26022689
}

modules/abstract-eth/src/lib/transactionBuilder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder {
621621
*
622622
* @returns {string} serialized sendMultiSig data
623623
*/
624-
private getSendData(): string {
624+
public getSendData(): string {
625625
if (!this._transfer) {
626626
throw new BuildTransactionError('Missing transfer information');
627627
}

0 commit comments

Comments
 (0)