2

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?

1 Answer 1

4

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"));
2
  • A theoricall/conceptual doubt: In your opinion who should take charge of the cost of ether in a transaction (for example the addUser of the solidity function) The "main account that was the one who deployed the contract". Or it should be every new user. It is added to the blockchain, a main account is passed ether / is generated new ether and is the same who is responsible for the payment of the transaction? This contract add a user to a mapping when is called from the Java server side Commented Jan 30, 2019 at 11:29
  • 1
    I think the use of unlocked personal account is generally a bad idea and very unsecured. Anybody with access to the node could siphon the account ... The question of who should pay the cost of transaction is complex because it raised the debate between decentralisation and adoption. But generally, we are saying more and more solution involving Meta-transactions (concept of relay to abstract the gas cost and make someone else pay for the end user) - See this post Commented Jan 30, 2019 at 11:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.