I am rather new to writing/using smart contracts in Java and I am currently trying to use my smart contract. I already wrote and tested it, compiled it, deployed it to Ropsten, and built my Java class from it using web3j-cli. So far, no problems.
Now, my contract features a function run(string memory x) that will return the string "ok" in case i pass the parameter "test" and "not ok" if any other string is passed. just for testing / exercise purposes for now.
Now what I am doing in my Java code is building a Web3j client and connect to Ethereum/Ropsten via my Infura account.
try { HttpService httpService = new HttpService(INFURA_ENDPOINT); // Infura endpoint for ropsten String auth = new String(":" + "yyyxxxzzz"); // Infura secret byte[] encodedAuth = Base64.encodeBase64(auth.getBytes(StandardCharsets.ISO_8859_1)); String authHeader = "Basic " + new String(encodedAuth); httpService.addHeader(HttpHeaders.AUTHORIZATION, authHeader); Web3j web3j = Web3j.build(new HttpService(INFURA_ENDPOINT)); String privateKeyString = "xyz"; // private key as exported from Metamask for my account in ropsten String address = "0x8e329a83B96fc80cc00C0acd5efbf78699b2F299"; // address for said account Credentials credentials = Credentials.create(privateKeyString); String contractAddress = "0xabcdef"; // address of the deployed contract in ropsten final BigInteger gasPrice = BigInteger.valueOf(2205000); final BigInteger gasLimit = BigInteger.valueOf(14300000); final ContractGasProvider gasProvider = new StaticGasProvider(gasPrice, gasLimit); final Test contract = Test.load(contractAddress, web3j, credentials, gasProvider); String a = contract.run("xyz").send(); System.out.println("run(xyz): " + a); } catch(Exception e) { System.out.println("Web3j: " + e.getMessage() + "\n"); e.printStackTrace(); } Now, explicitly calling the function run() looks like this in my generated contract Java class:
public RemoteFunctionCall<String> run(String name) { final Function function = new Function(FUNC_RUN, Arrays.<Type>asList(new org.web3j.abi.datatypes.Utf8String(name)), Arrays.<TypeReference<?>>asList(new TypeReference<Utf8String>() {})); return executeRemoteCallSingleValueReturn(function, String.class); } EDIT: Here's the Solidity code of the smart contract:
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.0; contract Test { function run(string memory name) public pure returns (string memory) { if(strcmp(name, "test")) { return "ok"; } return "not ok"; } function strcmp(string memory a, string memory b) internal pure returns (bool) { if(bytes(a).length != bytes(b).length) { return false; } else { return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b)); } } } Works as expected in remix.ethereum. This contract has been deployed in Ropsten at 0x8e329a83B96fc80cc00C0acd5efbf78699b2F299
Now, when I run this, a call such as contract.run("xyz")runs without problems and is also received by Infura - as I can easily check and verify in my dashboard. Yet as soon as I append a send() (same with sendAsync().get() and the lot) I get the following exception:
Exception in thread "main" java.lang.NoSuchMethodError: 'okhttp3.RequestBody okhttp3.RequestBody.create(java.lang.String, okhttp3.MediaType)' at org.web3j.protocol.http.HttpService.performIO(HttpService.java:154) at org.web3j.protocol.Service.send(Service.java:48) at org.web3j.protocol.core.Request.send(Request.java:87) at org.web3j.tx.RawTransactionManager.sendCall(RawTransactionManager.java:155) at org.web3j.tx.ManagedTransaction.call(ManagedTransaction.java:134) at org.web3j.tx.Contract.executeCall(Contract.java:292) at org.web3j.tx.Contract.executeCallSingleValueReturn(Contract.java:300) at org.web3j.tx.Contract.executeCallSingleValueReturn(Contract.java:311) at org.web3j.tx.Contract.lambda$executeRemoteCallSingleValueReturn$1(Contract.java:399) at org.web3j.protocol.core.RemoteCall.send(RemoteCall.java:42) at com.example.web3j.TestWeb3j.main(TestWeb3j.java:56) I'm mostly following the tutorials https://dzone.com/articles/blockchain-simplified-with-ethereum-example-with-j and https://trimplement.com/blog/2020/03/coding-smart-contracts-tutorial-infura/ here. As far as I understand here, a send() should call the contract's function and get me the returned string. I already asked this in the Infura-board with no success (https://community.infura.io/t/calling-functions-on-smart-contract-fails/2401).
What am I doing wrong?
Edit: Or asked differently: Can anyone provide me with a minimal working example of how to call a function in a smart contract (ideally, THIS function in THIS smart contract) on Ropsten using Web3j on Java 15 via Infura?
I am running all that in a SpringBoot 2.4.1 on Web3j 4.8.2 on Java 15
Edit: SpringBoot 2.4.3/Web3j 4.8.4 on IntelliJ IDEA 2020.3.2: Same problems
Edit 2: Proposed solution doesn't work unfortunately. Here is the entire project's code:
@SpringBootApplication public class SpringBootWeb3j { private final static String INFURA_ENDPOINT = "https://ropsten.infura.io/v3/"; private final static String INFURA_PROJECT_ID = "myProjectID"; private final static String INFURA_PROJECT_SECRET = "myProjectSecret"; public static void main(String[] args) { try { HttpService httpService = new HttpService(INFURA_ENDPOINT + INFURA_PROJECT_ID); String auth = new String(":" + INFURA_PROJECT_SECRET); byte[] encodedAuth = Base64.encodeBase64(auth.getBytes(StandardCharsets.ISO_8859_1)); String authHeader = "Basic " + new String(encodedAuth); httpService.addHeader(HttpHeaders.AUTHORIZATION, authHeader); Web3j web3j = Web3j.build(httpService); ECKeyPair ecKeyPair = Keys.createEcKeyPair(); Credentials credentials = Credentials.create(ecKeyPair); String contractAddress = "0x8e329a83B96fc80cc00C0acd5efbf78699b2F299"; final BigInteger gasPrice = BigInteger.valueOf(2205000); final BigInteger gasLimit = BigInteger.valueOf(14300000); final ContractGasProvider gasProvider = new StaticGasProvider(gasPrice, gasLimit); TransactionManager manager = new RawTransactionManager(web3j, credentials, 200, 500); final Test contract = Test.load(contractAddress, web3j, manager, gasProvider); String a = contract.run("xyz").send(); // <-- throws exception System.out.println("run(xyz): " + a); } catch(Exception e) { System.out.println("Problem with Web3j: " + e.getMessage() + "\n"); e.printStackTrace(); } SpringApplication.run(SpringBootWeb3j.class, args); } } Entire compiled Smart Contract:
/** * <p>Auto generated code. * <p><strong>Do not modify!</strong> * <p>Please use the <a href="https://docs.web3j.io/command_line.html">web3j command line tools</a>, * or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the * <a href="https://github.com/web3j/web3j/tree/master/codegen">codegen module</a> to update. * * <p>Generated with web3j version 4.5.16. */ @SuppressWarnings("rawtypes") public class Test extends Contract { public static final String BINARY = "608060405234801561001057600080fd5b506102a0806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80639352fad214610030575b600080fd5b61004361003e366004610134565b610059565b60405161005091906101f1565b60405180910390f35b606061008182604051806040016040528060048152602001631d195cdd60e21b8152506100c9565b156100a557506040805180820190915260028152616f6b60f01b60208201526100c4565b506040805180820190915260068152656e6f74206f6b60d01b60208201525b919050565b600081518351146100dc5750600061012e565b816040516020016100ed91906101d5565b604051602081830303815290604052805190602001208360405160200161011491906101d5565b604051602081830303815290604052805190602001201490505b92915050565b60006020808385031215610146578182fd5b823567ffffffffffffffff8082111561015d578384fd5b818501915085601f830112610170578384fd5b81358181111561018257610182610254565b604051601f8201601f19168101850183811182821017156101a5576101a5610254565b60405281815283820185018810156101bb578586fd5b818585018683013790810190930193909352509392505050565b600082516101e7818460208701610224565b9190910192915050565b6000602082528251806020840152610210816040850160208701610224565b601f01601f19169190910160400192915050565b60005b8381101561023f578181015183820152602001610227565b8381111561024e576000848401525b50505050565b634e487b7160e01b600052604160045260246000fdfea2646970667358221220eb9d0f71f0d251b9767f571c8b5add883a8f864291930492c9b9775e665bcfe664736f6c63430008000033"; public static final String FUNC_RUN = "run"; @Deprecated protected Test(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit); } protected Test(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { super(BINARY, contractAddress, web3j, credentials, contractGasProvider); } @Deprecated protected Test(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit); } protected Test(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider); } public RemoteFunctionCall<String> run(String name) { final Function function = new Function(FUNC_RUN, Arrays.asList(new org.web3j.abi.datatypes.Utf8String(name)), Arrays.asList(new TypeReference<Utf8String>() {})); return executeRemoteCallSingleValueReturn(function, String.class); } @Deprecated public static Test load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { return new Test(contractAddress, web3j, credentials, gasPrice, gasLimit); } @Deprecated public static Test load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { return new Test(contractAddress, web3j, transactionManager, gasPrice, gasLimit); } public static Test load(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { return new Test(contractAddress, web3j, credentials, contractGasProvider); } public static Test load(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { return new Test(contractAddress, web3j, transactionManager, contractGasProvider); } public static RemoteCall<Test> deploy(Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { return deployRemoteCall(Test.class, web3j, credentials, contractGasProvider, BINARY, ""); } @Deprecated public static RemoteCall<Test> deploy(Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { return deployRemoteCall(Test.class, web3j, credentials, gasPrice, gasLimit, BINARY, ""); } public static RemoteCall<Test> deploy(Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { return deployRemoteCall(Test.class, web3j, transactionManager, contractGasProvider, BINARY, ""); } @Deprecated public static RemoteCall<Test> deploy(Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { return deployRemoteCall(Test.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, ""); } } And while we're at it, here the build.gradle:
plugins { id 'org.springframework.boot' version '2.4.3' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '15' repositories { mavenCentral() } dependencies { implementation group: 'org.web3j', name: 'core', version: '4.8.4' implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' } test { useJUnitPlatform() } Project example available at https://gitlab.com/ErrorUsernameAlreadyTaken/springbootweb3j