区块链NFT开发常见问题:Only read operations are supported by this transaction manager 解决
问题描述
区块链NFT开发操作上链时报错如下
java.util.concurrent.ExecutionException: java.lang.UnsupportedOperationException: Only read operations are supported by this transaction manager
at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:396)
at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2073)
at net.sandbox.gpt.webapi.Web3Test.chkApprovedForAll(Web3Test.java:170)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.lang.UnsupportedOperationException: Only read operations are supported by this transaction manager
at org.web3j.tx.ReadonlyTransactionManager.sendTransaction(ReadonlyTransactionManager.java:45)
at org.web3j.tx.TransactionManager.executeTransaction(TransactionManager.java:81)
at org.web3j.tx.ManagedTransaction.send(ManagedTransaction.java:128)
at org.web3j.tx.Contract.executeTransaction(Contract.java:367)
at org.web3j.tx.Contract.executeTransaction(Contract.java:350)
at org.web3j.tx.Contract.executeTransaction(Contract.java:344)
at org.web3j.tx.Contract.executeTransaction(Contract.java:339)
at org.web3j.tx.Contract.lambda$executeRemoteCallTransaction$3(Contract.java:410)
at org.web3j.protocol.core.RemoteCall.send(RemoteCall.java:42)
at org.web3j.utils.Async.lambda$run$1(Async.java:38)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:833)
关键代码如下
public static Boolean setApprovedForAll(String token,String owner, String operator){
TransactionManager transactionManager = new ReadonlyTransactionManager(web3j, token);
ERC721 contract721 = ERC721.load(token, web3j, transactionManager, new DefaultGasProvider());
try {
TransactionReceipt tr = contract721.setApprovalForAll(operator,true).sendAsync().get();
if(tr.getBlockHash().isEmpty()) return false;
} catch (InterruptedException | ExecutionException e) {
logger.error("获取721uri异常", e);
return null;
}
return true;
}
执行到contract721.setApprovalForAll(operator,true).sendAsync().get(); 方法调用时就报了问题的错误
问题分析
报错原因是因为操作了事务,而传入合约的事务管理却是只读的实例ReadonlyTransactionManager,只读事务管理是不支持操作的,因此会报问题所列错误,其实除了我这个案例之外,其他任何上链操作时,传入的事务也不能是只读事务,否则都会报我这种错误
问题解决
将只读事务管理参数修改为RawTransactionManager 实例就好,参考代码如下:
TransactionManager transactionManager = new RawTransactionManager(web3j, getCredentials(),web3j.ethChainId().send().getChainId().longValue());
// TransactionManager transactionManager = new ReadonlyTransactionManager(web3j,token);
ERC721 contract721 = ERC721.load(token, web3j, transactionManager, new DefaultGasProvider());;