1

I've been learning about interacting with smart contracts through Android and the Web3j framework but have run into an issue.

I'm trying to mint an ERC721 token and send it to an address but it doesn't seem to be working. Whenever I try to invoke the method, it throws an exception and no token is created. Why isn't the token being generated?

Here's the Solidity code:

pragma solidity ^0.5.0;

import "../node_modules/@openzeppelin/contracts/token/ERC721/ERC721Full.sol";
import "../node_modules/@openzeppelin/contracts/drafts/Counters.sol";

 contract ExampleToken is ERC721Full {
     using Counters for Counters.Counter;
     Counters.Counter private _tokenIds;

     constructor() ERC721Full("ExampleToken", "EXT") public {
     }

     function mintUniqueToken(address _to, string memory _tokenURI) public returns (uint256) {
         _tokenIds.increment();

         uint256 newTokenId = _tokenIds.current();
         _mint(_to, newTokenId);
         _setTokenURI(newTokenId, _tokenURI);

         return newTokenId;

     }
 }

And here's the Java code:

private Web3j web3;
    private Credentials creds;
    private ExampleToken exampleContract;
    private String contractAddress;
    private DefaultGasProvider gasProvider;
    private String accountAddress;


    public EthNetwork() {
        web3 = Web3j.build(new HttpService(nodeURL));
        creds = Credentials.create(privateKey);
        contractAddress = erc721Address;
        gasProvider = new DefaultGasProvider();
        accountAddress = publicAccAddress;

        exampleContract = ExampleToken.load(contractAddress, web3, creds, gasProvider);
    }

    public String mintToken() throws Exception {
        TransactionReceipt transactionReceipt = exampleContract.mintUniqueToken(accountAddress, "EXT").send();
        String txHash = transactionReceipt.getTransactionHash();
        return txHash;
    }

Edit: Here's the result I get using printStackTrace() and more of the Java code. I should probably mention that I'm doing this on the Ropsten testnet and connecting via an Infura node.

W/System.err: android.os.NetworkOnMainThreadException
W/System.err:     at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1303)
        at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:91)
        at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:74)
        at java.net.InetAddress.getAllByName(InetAddress.java:752)
        at okhttp3.Dns$1.lookup(Dns.java:40)
        at okhttp3.internal.connection.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:185)
        at okhttp3.internal.connection.RouteSelector.nextProxy(RouteSelector.java:149)
        at okhttp3.internal.connection.RouteSelector.next(RouteSelector.java:84)
        at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:214)
        at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:135)
        at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:114)
        at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:126)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
        at okhttp3.RealCall.execute(RealCall.java:77)
        at org.web3j.protocol.http.HttpService.performIO(HttpService.java:160)
        at org.web3j.protocol.Service.send(Service.java:42)
        at org.web3j.protocol.core.Request.send(Request.java:81)
        at org.web3j.tx.RawTransactionManager.getNonce(RawTransactionManager.java:95)
        at org.web3j.tx.RawTransactionManager.sendTransaction(RawTransactionManager.java:118)
        at org.web3j.tx.TransactionManager.executeTransaction(TransactionManager.java:75)
        at org.web3j.tx.ManagedTransaction.send(ManagedTransaction.java:114)
        at org.web3j.tx.Contract.executeTransaction(Contract.java:362)
        at org.web3j.tx.Contract.executeTransaction(Contract.java:345)
        at org.web3j.tx.Contract.executeTransaction(Contract.java:339)
        at org.web3j.tx.Contract.executeTransaction(Contract.java:334)
        at org.web3j.tx.Contract.lambda$executeRemoteCallTransaction$3$Contract(Contract.java:401)
W/System.err:     at org.web3j.tx.-$$Lambda$Contract$w6-chY5FF2Qs682sB2cKs6onFiM.call(lambda)
        at org.web3j.protocol.core.RemoteCall.send(RemoteCall.java:42)
        at com.quintus.labs.datingapp.Main.EthNetwork.mintToken(EthNetwork.java:48)
        at com.quintus.labs.datingapp.Main.MainActivity.LikeBtn(MainActivity.java:241)
        at java.lang.reflect.Method.invoke(Native Method)
        at android.view.View$DeclaredOnClickListener.onClick(View.java:4706)
        at android.view.View.performClick(View.java:5623)
        at android.view.View$PerformClick.run(View.java:22427)
        at android.os.Handler.handleCallback(Handler.java:751)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6247)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:872)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:762)
  • Can you post the exception you are getting as well as more of the java code (how are you creating a web3 object / the example contract). – Steven V Nov 30 '19 at 5:20
0

I figured it out. Turns out the problem was a NetworkOnMainThreadException.
Trying to invoke the mintToken() method by itself on the MainActivity class causes the exception to be thrown. So I fixed it by extending the EthNetwork class to include AsyncTask:

public class EthNetwork extends AsyncTask<Void, Void, String>

Next I created a method that would mint a token in the background:

protected String doInBackground (Void... params) {
        try {
            return mintToken();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

Then I used .execute() in MainActivity and the token was successfully minted:

EthNetwork contract = new EthNetwork();  
contract.execute();

The Documentation for Android development contains more details

| improve this answer | |
0

I has faced same problem, and I want to share you solution, that probably be better than creating AsyncTask to every new interaction with smart-contract

Your code:

 public String mintToken() throws Exception {
        TransactionReceipt transactionReceipt = exampleContract.mintUniqueToken(accountAddress, "EXT").send();
        String txHash = transactionReceipt.getTransactionHash();
        return txHash;
    }

Note, that you are invoke function through .send(), therefore you are trying to invoke sync call in Main thread, which calls the exeption

At android you can use Completable future and .sendAsync() pattern to get rid of it, and make your code more readable and simple

CompletableFuture<TransactionReceipt> receipt = exampleContract.mintUniqueToken(accountAddress, "EXT").sendAsync();
            receipt.thenAccept(transactionReceipt -> {
                // get tx receipt only if successful
                String txHash = transactionReceipt.getTransactionHash(); 
                Log.d("receipt", "receipt"+transactionReceipt);
                Log.d("txhash", "txhash:" +txHash);

            }).exceptionally(transactionReceipt -> {
                return null;
            });  

This approach allow you to invoke smart-contracts functions asyncronossly, allow you to execute them from MainUI thread without typical problems

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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