[Ethereum] Attempting to transfer erc20 token, but keep encountering insufficient funds for gas * price + value

erc-20etherjavatokensweb3j

I'm attempting to transfer an erc20 token, but keep running into insufficiant funds for gas * price + value, regardles of how much eth I have, I also have an estimate function which returns a value thats several times lower than what I have in eth.

I'm using Infura.

My ETH balance is:

79475000000000000 (0,079475)

Here's my estimate output:

2020-08-31 14:49:26,036 INFO  [EthResource] (vert.x-worker-thread-1) ########## Entering doTokenEstimateTransactionFee...
2020-08-31 14:49:26,513 INFO  [Web3jService] (vert.x-worker-thread-1) >>>>>>>>>> (Token tx) DefaultGasProvider.GAS_LIMIT = 4300000
2020-08-31 14:49:26,673 INFO  [Web3jService] (vert.x-worker-thread-1) >>>>>>>>>> (Token tx) ethEstimateGas.amountUsed = 21584
2020-08-31 14:49:26,674 INFO  [Web3jService] (vert.x-worker-thread-1) >>>>>>>>>> (Token tx) ethGasPrice.gasPrice = 217000000000
2020-08-31 14:49:26,674 INFO  [Web3jService] (vert.x-worker-thread-1) >>>>>>>>>> (Token tx) estimated tx fee = 4683728000000000
2020-08-31 14:49:26,674 INFO  [EthResource] (vert.x-worker-thread-1) ########## (success) Leaving doTokenEstimateTransactionFee...

So the fee should be 0,004683728, which is less than my balance.

Here's my code:

@Synchronized
fun TokenTransferFrom(TokenTransferFromDto: TokenTransferFromDto): ResultDto {
    return try {
        val sourceCredentials: Credentials = Util.generateCredentialsBip32(TokenTransferFromDto.sourceMnemonic)
        return if (TokenTransferFromDto.sourcePrivateKey == sourceCredentials.ecKeyPair.privateKey.toString(16)) {
            val fastRawTransactionManager = FastRawTransactionManager(web3j, sourceCredentials, NoOpProcessor(web3j))
            // Load contract by source
            val TokenSource = load(infuraTokenContractAddress, web3j, fastRawTransactionManager, DefaultGasProvider())
            val transactionReceipt = TokenSource.transfer(TokenTransferFromDto.destination, TokenTransferFromDto.amount).send()
            if (!transactionReceipt.isStatusOK) {
                return wrapError("transactionReceipt.isStatusOK false", transactionReceipt.status)
            }
            logger.info(">>>>>>>>>> TokenTransferFrom txHash = ${transactionReceipt.transactionHash}")
            ResultDto("success", null, "", transactionReceipt.transactionHash, null, null, null)
        } else {
            wrapError("", "Invalid mnemonic or private key")
        }
    } catch (e: Exception) {
        logger.error(">>>>>>>>>> EXCEPTION: ${e.message}")
        wrapError("", e.message.toString())
    }
}

Can someone tell me what I have to change to be able to specify my own GAS_LIMIT as I'm stumped?

Here's the error that I endlessly keep encountering:

2020-08-31 14:54:34,787 ERROR [Web3jService] (vert.x-worker-thread-4) >>>>>>>>>> EXCEPTION: Error processing transaction request: insufficient funds for gas * price + value

I'm completely new to kotlin, java, so sorry if I'm dumb. Thanks.

Best Answer

I managed to resolve the issue, it was caused by the app setting the default GAS_PRICE and GAS_LIMIT as values that we're too high or too low.

For example, the web3j API had the GAS_LIMIT for the contract set at 4,300,000, but the GAS_PRICE at 22 GWEI (22,000,000,000 WEI), which was well bellow the network average gas price which was 450 GWEI (450,000,000,000 WEI).

First of all, I adjusted the GAS_PRICE to match the current average network GAS_PRICE, but still encountered the same "insufficient funds for gas * price + value" error, at which point I figured that the Transaction cost that the API was trying to reserve (the transaction is able to reserve more than it will spend) was greater than the balance I had

(450,000,000,000 * 4,300,000 = 1,935,000,000,000,000,000 WEI [1.935 ETH]).

So the next step was reducing the GAS_LIMIT, for regular ETH transactions it's usually set at 21,000, but token transactions are more expensive, so I set it at 30,000. Finally, my transaction went through and appeared on the network, but after about 10 minutes the transaction failed with the error OUT OF GAS. This meant the GAS_LIMIT I specified was too low, so I increased it to 100,000. This meant that at the current GAS_PRICE the max cost of the transaction was ~20$. The transaction went through again and got confirmed within a minute, costing me 9$.

Here's the updated code in case anyone is wondering how I updated the GAS_LIMIT and GAS_PRICE.

@Synchronized
fun TokenTransferFrom(TokenTransferFromDto: TokenTransferFromDto): ResultDto {
    return try {
        val sourceCredentials: Credentials = Util.generateCredentialsBip32(TokenTransferFromDto.sourceMnemonic)
        return if (TokenTransferFromDto.sourcePrivateKey == sourceCredentials.ecKeyPair.privateKey.toString(16)) {
            val fastRawTransactionManager = FastRawTransactionManager(web3j, sourceCredentials, NoOpProcessor(web3j))
            // Load contract by source
            val TokenSource = load(infuraTokenContractAddress, web3j, fastRawTransactionManager, DefaultGasProvider())

            //
            // NEW CODE STARTS HERE
            //
            
            // Generate a new gasProvider using the current network gasPrice and our own gasLimit
            val gasProvider = StaticGasProvider(TokenSource.requestCurrentGasPrice(), BigInteger.valueOf(100000))
            // Set our own gasProvider as the current gasProvider
            TokenSource.setGasProvider(gasProvider)
            
            //
            // NEW CODE ENDS HERE
            //
            
            val transactionReceipt = TokenSource.transfer(TokenTransferFromDto.destination, TokenTransferFromDto.amount).send()
            if (!transactionReceipt.isStatusOK) {
                return wrapError("transactionReceipt.isStatusOK false", transactionReceipt.status)
            }
            logger.info(">>>>>>>>>> TokenTransferFrom txHash = ${transactionReceipt.transactionHash}")
            ResultDto("success", null, "", transactionReceipt.transactionHash, null, null, null)
        } else {
            wrapError("", "Invalid mnemonic or private key")
        }
    } catch (e: Exception) {
        logger.error(">>>>>>>>>> EXCEPTION: ${e.message}")
        wrapError("", e.message.toString())
    }
}
Related Topic