下面将提供一个完整的Java调用智能合约的示例,包括环境配置、合约部署和调用方法。
1. 环境准备
添加依赖
<!-- Maven 依赖 -->
<dependencies>
<dependency>
<groupId>org.web3j</groupId>
<artifactId>core</artifactId>
<version>4.9.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.30</version>
</dependency>
</dependencies>
2. 智能合约示例
Solidity合约代码
// SimpleStorage.sol
pragma solidity ^0.8.0;
contract SimpleStorage {
uint256 private value;
string private text;
event ValueChanged(uint256 newValue);
event TextChanged(string newText);
function setValue(uint256 _value) public {
value = _value;
emit ValueChanged(_value);
}
function getValue() public view returns (uint256) {
return value;
}
function setText(string memory _text) public {
text = _text;
emit TextChanged(_text);
}
function getText() public view returns (string memory) {
return text;
}
}
3. Java调用代码
主要工具类
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.gas.DefaultGasProvider;
import java.math.BigInteger;
public class ContractCaller {
private Web3j web3j;
private Credentials credentials;
private String contractAddress;
public ContractCaller(String nodeUrl, String privateKey) {
this.web3j = Web3j.build(new HttpService(nodeUrl));
this.credentials = Credentials.create(privateKey);
}
/**
* 部署合约
*/
public String deployContract() throws Exception {
SimpleStorage contract = SimpleStorage.deploy(
web3j,
credentials,
new DefaultGasProvider()
).send();
this.contractAddress = contract.getContractAddress();
System.out.println("合约部署成功,地址: " + contractAddress);
return contractAddress;
}
/**
* 加载已部署的合约
*/
public SimpleStorage loadContract(String contractAddress) {
return SimpleStorage.load(
contractAddress,
web3j,
credentials,
new DefaultGasProvider()
);
}
/**
* 调用合约的写方法
*/
public void setValue(String contractAddress, BigInteger value) throws Exception {
SimpleStorage contract = loadContract(contractAddress);
TransactionReceipt receipt = contract.setValue(value).send();
System.out.println("设置值成功,交易哈希: " + receipt.getTransactionHash());
}
/**
* 调用合约的读方法
*/
public BigInteger getValue(String contractAddress) throws Exception {
SimpleStorage contract = loadContract(contractAddress);
BigInteger value = contract.getValue().send();
System.out.println("获取的值: " + value);
return value;
}
/**
* 设置文本
*/
public void setText(String contractAddress, String text) throws Exception {
SimpleStorage contract = loadContract(contractAddress);
TransactionReceipt receipt = contract.setText(text).send();
System.out.println("设置文本成功,交易哈希: " + receipt.getTransactionHash());
}
/**
* 获取文本
*/
public String getText(String contractAddress) throws Exception {
SimpleStorage contract = loadContract(contractAddress);
String text = contract.getText().send();
System.out.println("获取的文本: " + text);
return text;
}
}
测试类
import java.math.BigInteger;
public class ContractCallerTest {
public static void main(String[] args) {
try {
// 配置参数
String nodeUrl = "https://mainnet.infura.io/v3/YOUR_PROJECT_ID"; // 以太坊节点
String privateKey = "YOUR_PRIVATE_KEY"; // 私钥
ContractCaller caller = new ContractCaller(nodeUrl, privateKey);
// 1. 部署合约
String contractAddress = caller.deployContract();
// 2. 设置数值
caller.setValue(contractAddress, BigInteger.valueOf(12345));
// 3. 读取数值
BigInteger value = caller.getValue(contractAddress);
// 4. 设置文本
caller.setText(contractAddress, "Hello Web3j!");
// 5. 读取文本
String text = caller.getText(contractAddress);
} catch (Exception e) {
e.printStackTrace();
}
}
}
4. 使用Web3j命令行工具生成Java包装类
生成Java包装类步骤:
# 1. 安装web3j命令行工具
curl -L get.web3j.io | sh
# 2. 编译Solidity合约
solc SimpleStorage.sol --bin --abi --optimize -o build/
# 3. 生成Java包装类
web3j generate solidity -b build/SimpleStorage.bin -a build/SimpleStorage.abi -o src/main/java -p com.yourpackage.contracts
生成的Java包装类(部分):
// 这是web3j自动生成的合约包装类
public class SimpleStorage extends Contract {
public static final String BINARY = "0x606060..."; // 合约字节码
public SimpleStorage(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
super(BINARY, contractAddress, web3j, credentials, contractGasProvider);
}
// 合约方法...
}
5. 高级功能示例
事件监听
public class EventListener {
public void listenToEvents(String contractAddress, Web3j web3j, Credentials credentials) throws Exception {
SimpleStorage contract = SimpleStorage.load(
contractAddress,
web3j,
credentials,
new DefaultGasProvider()
);
// 监听ValueChanged事件
contract.valueChangedEventFlowable(DefaultBlockParameterName.EARLIEST,
DefaultBlockParameterName.LATEST).subscribe(event -> {
System.out.println("值改变事件: " + event.newValue);
});
// 监听TextChanged事件
contract.textChangedEventFlowable(DefaultBlockParameterName.EARLIEST,
DefaultBlockParameterName.LATEST).subscribe(event -> {
System.out.println("文本改变事件: " + event.newText);
});
}
}
错误处理
public class ContractCallerWithErrorHandling {
public void safeContractCall(String contractAddress, BigInteger value) {
try {
ContractCaller caller = new ContractCaller("YOUR_NODE_URL", "YOUR_PRIVATE_KEY");
caller.setValue(contractAddress, value);
} catch (Exception e) {
if (e.getMessage().contains("insufficient funds")) {
System.out.println("余额不足");
} else if (e.getMessage().contains("gas required exceeds allowance")) {
System.out.println("Gas不足");
} else {
System.out.println("调用失败: " + e.getMessage());
}
}
}
}
6. 配置说明
重要配置参数
public class Web3jConfig {
// 不同的网络配置
public static final String MAINNET = "https://mainnet.infura.io/v3/YOUR_PROJECT_ID";
public static final String ROPSTEN = "https://ropsten.infura.io/v3/YOUR_PROJECT_ID";
public static final String GANACHE = "http://localhost:7545";
// Gas配置
public static class CustomGasProvider extends DefaultGasProvider {
@Override
public BigInteger getGasPrice() {
return BigInteger.valueOf(20_000_000_000L); // 20 Gwei
}
@Override
public BigInteger getGasLimit() {
return BigInteger.valueOf(300_000L);
}
}
}
注意事项
- 私钥安全: 永远不要在代码中硬编码私钥,使用环境变量或安全配置
- Gas费用: 合理设置Gas价格和限制
- 错误处理: 完善的异常处理机制
- 网络选择: 根据需求选择合适的网络(主网、测试网、本地网络)