[Ethereum] Error processing transaction request: sender doesn’t have enough funds to send tx

ganacheweb3j

In a Java project im testing the web3j library. there I have a method that generate a new account to the blockchain

public String url = "http://localhost:7545/";
public Admin admin = Admin.build(new HttpService(url));

NewAccountIdentifier newAccountIdentifier = admin.personalNewAccount(key).send();
String address = newAccountIdentifier.getAccountId();

        if (newAccountIdentifier.hasError()) {
            Error error = newAccountIdentifier.getError();
            int code = error.getCode();
            String message = error.getMessage();
            throw new IllegalStateException(code + ": " + message);
        }

When I generate the user I instantiate a deployed contract from Remix IDE to Ganache Blockchain.

UsersContract userContract = UsersContract.load("0xc85e6e4b979d05a9b5adbac3e0e7d68b632460d1", admin, credentials, new BigInteger("240000"), new BigInteger("2400000"));

TransactionReceipt transactionReceipt = userContract.addUser(address).send();

The account is generated(appear in the remix accounts field) and the Java code arrive to the deployed contract. because I have the following error:

java.lang.RuntimeException: Error processing transaction request: sender doesn't have enough funds to send tx. The upfront cost is: 576000000000 and the sender's account only has: 0

So I create a method that from the first ganache account(with 100 eth) give to the new account a amount of ether. The method works fine

enter image description here

So the flow that i´m following is:

  • Create new account
  • Send eth from a pregenerated account with ether to the new account
  • Load the deployed contract with the address
  • When this line execute (TransactionReceipt transactionReceipt = userContract.addUser(address).send();) I get the exception

The main problem is that I can´t understand what´s happend. First I was thinking that the new user dont have ether so can´t complete the transaction. I make a method to give ether and the problem persist. So now im blocked. if I make the transaction without web3j and I call from he Remix IDE the transaction is done and the gas cost is 70853, and not the the 576…. number that return me the Java exception

Other question that I have is that when I load the contract the credential that I load is the new account credential. It has more sense to load always the account that set to default(default I mean the account that deployed the contract, because inside the the contract appear a onlyOwner modifier), or it has sense to load the credentilas of the new address?

Any help?

Best Answer

There is different way to send transactions to a smart contract through node:

  • (1) A client signs a transaction with his account (private key) and send it to the node. Then the node only broadcasts it to the network. Transaction fees are paid by the client.

  • (2) A client sends a clear transaction (no signature) and ask the node to sign and broadcast the transaction with one of the account managed by this node. That's the all concept of personal unlocked account which should be used only for personal use or testing. Transaction fees are paid by the node.

By doing this (below), you basically create an account directly on the node (2)

NewAccountIdentifier newAccountIdentifier = admin.personalNewAccount(key).send();
String address = newAccountIdentifier.getAccountId();

If you want to create and manage an account only on the client (java program), you would have to do this:

ECKeyPair keyPair = Keys.createEcKeyPair(); 
Credentials credentials = Credentials.create(privateKey);

In web3j you have the concept of TransactionManager responsible of executing the transactions and you have different type of TransactionManager:

  • RawTransactionManager can sign and send transaction (1)
  • ReadonlyTransactionManager can only read data (no transaction)
  • ClientTransactionManager can only send a clear transaction to the node and let the node sign it (2)

When you load the contract like you did UsersContract userContract = UsersContract.load(address, admin, credential, ...), behind the scene, Web3J instantiate a RawTransactionManager which will sign the transaction (using the credentials - private key) before sending it to the node.

UsersContract userContract = UsersContract.load("0xc85e6e4b979d05a9b5adbac3e0e7d68b632460d1", admin, credentials, new BigInteger("240000"), new BigInteger("2400000"));

But in your case, you actually need a ClientTransactionManager, so you can directly pass it to the UsersContract.load(...) method like this

ClientTransactionManager transactionManager = new ClientTransactionManager(web3j, newAccountIdentifier.getAccountId());
UsersContract userContract = UsersContract.load("0xc85e6e4b979d05a9b5adbac3e0e7d68b632460d1", admin, transactionManager, new BigInteger("240000"), new BigInteger("2400000"));
Related Topic