文章是本人学习过程翻译,原文来自官方文档:https://web3j.readthedocs.io/en/latest/#
官网:https://web3j.io/
官方GitHub:https://github.com/web3j/web3j
官方demo:https://github.com/web3j/web3j/tree/master/integration-tests
文档版本v3.4.0。
有三种语言可以开发智能合约:
- solidity
- serpent
- LISP Like Language (LLL)
开始solidity(Getting started with Solidity)
编译solidity(Compiling Solidity source code)
你也可以编译solidityt的代码通过Ethereum客户Geth和Parity等,solidty编译器安装在这些客户端,通过使用json - rpc的方法eth_compileSolidity,web3j也支持这个方法。
更多编译solidity,请参考solidity文档
部署合约和交互(Deploying and interacting with smart contracts)
有两种方式来部署合约和交互:
- 使用Solidity smart contract wrappers 可以让你使用生成的类包快速与合约交互。
- 原始的交易方法,创建合约,与合约交互,查询合约状态
智能合约例子
我们在项目目录codegen/src/test/resources/solidity提供了一些solidity智能合约例子。
也提供一些代码演示部署合约、与合约交互,在integration-tests/src/test/java/org/web3j/protocol/scenarios
ERC-20代币合约标准
有两个组织提供了ERC-20合约的实现:
- ConsenSys - ConsenSys的Tokens project.
- Open Zepplin - Open Zepplin的GitHub地址
web3j提供了ConsenSys合约的实现,合约代码在目录codegen/src/test/resources/solidity/contracts。
下面是Solidity smart contract wrappers生成类包的方式调用代币合约的实现:
- HumanStandardTokenGeneratedIT
- HumanStandardToken
如果你不想使用生成的类包,可以使用JSON-RPC calls的方式实现合约调用,参考HumanStandardTokenIT
solidity智能合约类包(Solidity smart contract wrappers)
自动生成的类包支持所有与智能合约的操作,包括
- 构建和部署合约
- 发起交易和事件
- 调用constant函数
- 合约校验
构建和部署合约(Construction and deployment)
创建一个新的合约对象
YourSmartContract contract = YourSmartContract.deploy(
, , GAS_PRICE, GAS_LIMIT,
[,]
, ..., ).send();
构建一个已经存在的合约对象
YourSmartContract contract = YourSmartContract.load(
"0x|", web3j, credentials, GAS_PRICE, GAS_LIMIT);
合约校验(Contract validity)
如果你要校验你的合约对象是否存在以太坊和有效,你可以通过校验合约的字节码与合约地址是否匹配。使用下面方法就可以完成:
contract.isValid();
交易管理(Transaction Managers)
Transaction Managers类是设置web3j连接到以太坊客户端的方式,有如下几个类:
- TransactionManager 交易管理抽象类
- RawTransactionManager 线下交易时使用的交易管理类
- ClientTransactionManager 负责签名交易到客户端的交易管理类
- ReadonlyTransactionManager 只读交易管理类
指定交易的Chain Id(Specifying the Chain Id on Transactions (EIP-155))
RawTransactionManager 类带有一个可选的参数 chainId 来指定交易是的链id,用来区分不同的链(主网/测试网),防止一个链的交易被广播到另外一个链。
TransactionManager transactionManager = new RawTransactionManager(
web3j, credentials, ChainId.MAIN_NET);
默认情况下,web3j没有指定chain id,推荐你手动指定。
如下方法可以获取你连接的客户端的chain id:
web3j.netVersion().send().getNetVersion();
交易收据处理(Transaction Receipt Processors)
默认情况下,提交一个交易到客户端后,web3j将会轮询客户端直到收到TransactionReceipt对象,表示交易已经被区块链打包确认。
如果你发送异步交易,会有一个并行的线程去轮询客户单。
如果你想减少轮询消耗,web3j提供了TransactionReceiptProcessors.
web3j提供了一些交易收据的处理器:
- PollingTransactionReceiptProcessor web3j默认的收据处理器,为每个等待交易定期轮询获取收据。
- QueuingTransactionReceiptProcessor 内部有一个所有等待交易的队列,处理器会定期查询收据,如果有交易收据被发现,会调用一个回调函数。
- NoOpProcessor 提供一个只包含交易哈希的EmptyTransactionReceipt,这是为了不让web3j去轮询交易收据。
注意:在QueuingTransactionReceiptProcessor初始化时也提供了一个只有交易哈希的EmptyTransactionReceipt。
如果你不想使用默认的PollingTransactionReceiptProcessor处理器,可以修改处理器:
TransactionReceiptProcessor transactionReceiptProcessor = new QueuingTransactionReceiptProcessor(web3j, new Callback() {
@Override
public void accept(TransactionReceipt transactionReceipt) {
// process transactionReceipt
}
@Override
public void exception(Exception exception) {
// handle exception
}
TransactionManager transactionManager = new RawTransactionManager(
web3j, credentials, ChainId.MAIN_NET, transactionReceiptProcessor);
如果你想获得更进一步的了解,FastRawTransactionManagerIT演示了轮询和队列处理。
调用交易和事件(Invoking transactions and events)
直接调用与合约函数同名的方法就可以发起交易,虽然因为打包延迟的问题无法获得合约调用的返回值,但通过Transaction Receipt来关联交易,一样可以获取交易结果。
TransactionReceipt transactionReceipt = contract.someMethod(
,
...).send();
交易收据有以下两个作用:
- 可以获取打包交易的区块信息
- solidity event可以被触发来记录交易的日志,这些日志可以被提取。
在生成的类包里面也有
EventValues eventValues = contract.processSomeEvent(transactionReceipt);
也可以使用过滤器
contract.someEventObservable(startBlock, endBlock).
.subscribe(event -> ...);
更进一步的了解,请查看Filters and Events.
注意:索引的array,bytes和string等solidity参数类型,keccak - 256的散列值将被返回,看到文档了解更多信息。
调用静态函数(Calling constant methods)
使用生成的函数可以直接调用合约的constant函数
Type result = contract.someMethod(
动态的Gas Price和Limit(Dynamic gas price and limit)
你可以创建 ContractGasProvider ,根据调用不同的函数来指定不同的gas price和limit。
例子:
Greeter greeter = new Greeter(...);
greeter.setGasProvider(new DefaultGasProvider() {
@Override
public BigInteger getGasPrice(String contractFunc) {
switch (contractFunc) {
case Greeter.FUNC_GREET: return BigInteger.valueOf(22_000_000_000L);
case Greeter.FUNC_KILL: return BigInteger.valueOf(44_000_000_000L);
default: throw new NotImplementedException();
}
}
@Override
public BigInteger getGasLimit(String contractFunc) {
switch (contractFunc) {
case Greeter.FUNC_GREET: return BigInteger.valueOf(4_300_000);
case Greeter.FUNC_KILL: return BigInteger.valueOf(5_300_000);
default: throw new NotImplementedException();
}
}
});
案例(Examples)
https://github.com/web3j/web3j/tree/master/integration-tests