Tokens

無法從 Web3j 合約包裝器呼叫智能合約功能

  • December 22, 2021

我一直在學習如何通過 Android 和 Web3j 框架與智能合約進行互動,但遇到了一個問題。

我正在嘗試鑄造一個 ERC721 令牌並將其發送到一個地址,但它似乎不起作用。每當我嘗試呼叫該方法時,它都會引發異常並且不會創建任何令牌。為什麼沒有生成令牌?

這是 Solidity 程式碼:

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;

    }
}

這是Java程式碼:

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;
   }

編輯:這是我使用 printStackTrace() 和更多 Java 程式碼得到的結果。我可能應該提到我正在 Ropsten 測試網上執行此操作並通過 Infura 節點進行連接。

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)

我想到了。原來問題是 NetworkOnMainThreadException。

嘗試在 MainActivity 類上自行呼叫 mintToken() 方法會導致引發異常。所以我通過擴展 EthNetwork 類以包含 AsyncTask 來修復它:

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

接下來我創建了一個在後台生成令牌的方法:

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

然後我在 MainActivity 中使用了 .execute() 並成功鑄造了令牌:

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

Android 開發文件包含更多詳細資訊

我遇到了同樣的問題,我想分享你的解決方案,這可能比為每次與智能合約的新互動創建 AsyncTask 更好

你的程式碼:

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

請注意,您是通過 呼叫函式.send(),因此您試圖在呼叫異常的主執行緒中呼叫同步呼叫

在 android 你可以使用Completable future.sendAsync()pattern 來擺脫它,並使你的程式碼更具可讀性和簡單性

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;
           });  

這種方法允許您非同步呼叫智能合約函式,允許您從 MainUI 執行緒執行它們而不會出現典型問題

引用自:https://ethereum.stackexchange.com/questions/77923