Web3j – Troubleshooting Unable to Retrieve Value from sendFunds Future

androidjavaweb3j

I have been trying to send funds from one ethereum account to another using web3j to test that it works on Android. I am using an Infura cloud instance.

First, it appears I need to send it async because Android requires that you do so. In the code down below I have tried to do so – but it fails on the last step, when I try to get from the Future. If I do not try to get the value then there is no exception, but I obviously cannot do anything with the value that I want to do.

        final String FROM_ADDRESS = "0x6861B070f43842FC16eAD07854eE5D91B9D27C13";
    final String TO_ADDRESS = "0x31B98D14007bDEe637298086988A0bBd31184523";

    //Credentials credentials = obtainCredentials(WALLET_DIRECTORY);

    Callable<TransactionReceipt> task = new Callable<TransactionReceipt>() {
        @Override
        public TransactionReceipt call() throws Exception {
            Web3j web3 = Web3jFactory.build(
                    new HttpService("https://rinkeby.infura.io/SxLC8uFzMPfzwnlXHqx9")
            );
            Log.d(TAG, web3.ethGasPrice().getJsonrpc());

            ClientTransactionManager clientTransactionManager =
                    new ClientTransactionManager(web3, FROM_ADDRESS);
            Log.d(TAG, clientTransactionManager.getFromAddress());

            org.web3j.tx.Transfer tran = new org.web3j.tx.Transfer(web3, clientTransactionManager);
            Log.d(TAG, String.valueOf(tran.getSyncThreshold()));

            RemoteCall<TransactionReceipt> rc = tran.sendFunds(
                    TO_ADDRESS,
                    BigDecimal.valueOf(1.0),
                    Convert.Unit.ETHER
            );
            Log.d(TAG, String.valueOf(rc.toString()));
            return rc.send();
        }
    };
    Future<TransactionReceipt> future = Async.run(task);
    return future.get();

It comes with this exception:

11-19 12:49:03.418 32442-32442/com.example.graeme.beamitup W/System.err: java.util.concurrent.ExecutionException: org.web3j.protocol.exceptions.ClientConnectionException: Invalid response received: okhttp3.internal.http.RealResponseBody@3c286ab
11-19 12:49:03.418 32442-32442/com.example.graeme.beamitup W/System.err:     at java.util.concurrent.FutureTask.report(FutureTask.java:94)
11-19 12:49:03.419 32442-32442/com.example.graeme.beamitup W/System.err:     at java.util.concurrent.FutureTask.get(FutureTask.java:164)
11-19 12:49:03.419 32442-32442/com.example.graeme.beamitup W/System.err:     at com.example.graeme.beamitup.Transfer.sendTransfer(Transfer.java:183)
11-19 12:49:03.419 32442-32442/com.example.graeme.beamitup W/System.err:     at com.example.graeme.beamitup.MainActivity.onCreate(MainActivity.java:26)
11-19 12:49:03.419 32442-32442/com.example.graeme.beamitup W/System.err:     at android.app.Activity.performCreate(Activity.java:6272)

It points out an invalid response from the HTTP transfer – but shouldn't this be happening even without trying the Future.get? The code executes either way, correct? What have I done wrong to create this error?

Best Answer

1) This line is probably not doing what you expect:

Log.d(TAG, String.valueOf(rc.toString()));

rc has type RemoteCall<TransactionReceipt>, which means it needs to be executed. Instead, your code converts the invocation to a String (twice): once from the toString() and once from String.valueOf.

2) You create a future, then wait for it to complete, and I know you know this is pointless. I dispensed with the future, no idea why Android insists on a future. My simpler code gives the same result as your code, so at least we know that the future is not introducing the problem you report.

3) The error arises after this JSON-RPC payload is sent:

{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":1}

The ClientTransactionManager.sendTransaction() method dies when it attempts to execute:

return web3j.ethSendTransaction(transaction)
            .send();

Although web3j.ethSendTransaction(transaction) seems to work fine, the chained send() method returns an unsuccessful response. I suspect authentication is required, but of course no credentials were provided so the transaction was rejected.

I don't work in Android, so here is the code I worked with:

import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.RemoteCall;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.ClientTransactionManager;
import org.web3j.utils.Convert;

import java.math.BigDecimal;

public class SO {
    private static final String FROM_ADDRESS = "0x6861B070f43842FC16eAD07854eE5D91B9D27C13";
    private static final String TO_ADDRESS = "0x31B98D14007bDEe637298086988A0bBd31184523";

    public static void main(String[] args) throws Exception {
        TransactionReceipt value = new SO().test();
        System.out.println(value);
    }

    private TransactionReceipt test() throws Exception {
        //Credentials credentials = obtainCredentials(WALLET_DIRECTORY);

        Web3j web3 = Web3j.build(
            new HttpService("https://rinkeby.infura.io/SxLC8uFzMPfzwnlXHqx9")
        );
        System.out.println("ethGasPrice=" + web3.ethGasPrice().getJsonrpc());

        ClientTransactionManager clientTransactionManager =
            new ClientTransactionManager(web3, FROM_ADDRESS);
        System.out.println("From address=" + clientTransactionManager.getFromAddress());

        org.web3j.tx.Transfer transfer = new org.web3j.tx.Transfer(web3, clientTransactionManager);
        System.out.println("transfer=" + String.valueOf(transfer.getSyncThreshold()));

        RemoteCall<TransactionReceipt> rc = transfer.sendFunds(
            TO_ADDRESS,
            BigDecimal.valueOf(1.0),
            Convert.Unit.ETHER
        );
        TransactionReceipt receipt = rc.send();
        System.out.println("receipt=" + receipt.toString());
        return receipt;
    }
}
Related Topic