Tokens
無法從 Web3j 合約包裝器呼叫智能合約功能
我一直在學習如何通過 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();
我遇到了同樣的問題,我想分享你的解決方案,這可能比為每次與智能合約的新互動創建 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 執行緒執行它們而不會出現典型問題