05-web3j智能合约(Smart Contracts)


文章是本人学习过程翻译,原文来自官方文档: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。


有三种语言可以开发智能合约:

  1. solidity
  2. serpent
  3. 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)

有两种方式来部署合约和交互:

  1. 使用Solidity smart contract wrappers 可以让你使用生成的类包快速与合约交互。
  2. 原始的交易方法,创建合约,与合约交互,查询合约状态

智能合约例子

我们在项目目录codegen/src/test/resources/solidity提供了一些solidity智能合约例子。

也提供一些代码演示部署合约、与合约交互,在integration-tests/src/test/java/org/web3j/protocol/scenarios

ERC-20代币合约标准

有两个组织提供了ERC-20合约的实现:

  1. ConsenSys - ConsenSys的Tokens project.
  2. 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();

交易收据有以下两个作用:

  1. 可以获取打包交易的区块信息
  2. solidity event可以被触发来记录交易的日志,这些日志可以被提取。

在生成的类包里面也有的事件,可以使用索引参数和非索引参数来提取收据,返回值是 EventValues 对象.

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(, ...).send();

动态的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

你可能感兴趣的:(05-web3j智能合约(Smart Contracts))