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