# Web3j 读取 eth log

By [liubo](https://paragraph.com/@liubo) · 2022-07-05

---

最近在做 nft 相关的小项目，需要通过解析一些 eth log 来触发后台的相关逻辑。看了下 web3j 发现有两种方式可以实现这个解析。

### 1、直接利用 web3j 生成 wrapper code

    编译 solidity 代码
    
    solc <contract>.sol --bin --abi --optimize -o <output-dir>/
    
    solc contract.sol --bin --abi --optimize -o build/ --include-path ./node_modules --base-path ./
    
    生成 wrapper code
    
    web3j generate solidity -b /path/to/<smart-contract>.bin -a /path/to/<smart-contract>.abi -o /path/to/src/main/java -p com.your.organisation.name
    

生成的代码里有直接相关的 event 订阅和查询方法，可以直接使用。这里也直接会把相关的日志数据解析成对应的 xxEventResponse，可以直接使用。

    new Contract(contractAddress,web3j).xxxEventFlowable(filter).subscribe(x->{process(x);});
    

### 2、手动订阅解析

如果你跟我一样，需要监控多个合约，多种 event，那就可以自己定制 filter 进行订阅。

     List<String> contracts = Lists.newArrayList(contractAddressOne, contractAddressTwo);
        EthFilter filter =
            new EthFilter(
                DefaultBlockParameter.valueOf(startNumber),
                DefaultBlockParameterName.LATEST,
                contracts);
        web3j
            .ethLogFlowable(filter)
            .doOnError(
                ex -> {
                  log.error("log trace failed", ex);
                })
            .subscribe(
                eventLog -> {
                  process(eventLog);
                });
      }        
    

跟第一种方式不一样，这里拿到的 event 数据都比较原始，需要我们手动来进行解析。

event log 里的数据分为两类，一种是 topic，一种是 data。 topic 对应 solidity 中 indexed 的值，data 则对应未 indexed 的值。

举个例子，以下这个 event 定义里的 operator 就属于 topic； itemIds 和 tokenIds 则属于 data。 另外，topic\[0\] 是函数签名，所以取数据的时候是从下标 1 开始。

    event Mint(address indexed operator, uint64[] itemIds, uint256 [] tokenIds);
    

使用 web3j 解析这个日志数据，我们可以先定义好对应的 Event

    public static final Event MINT =
        new Event(
            "Mint",
            Arrays.asList(
                new TypeReference<Address>(true) {},
                new TypeReference<DynamicArray<Uint64>>() {},
                new TypeReference<DynamicArray<Uint256>>() {}));
    

然后将对应的 log 数据转换一下

    public void convert(Log eventLog) {
      Address address =
          (Address)
              FunctionReturnDecoder.decodeIndexedValue(
                  eventLog.getTopics().get(1), new TypeReference<Address>() {});
    
      List<Type> params =
          FunctionReturnDecoder.decode(
              eventLog.getData(), EthEvent.MINT.getNonIndexedParameters());
      List<Long> itemIds =
          ((DynamicArray<Uint64>) params.get(0))
              .getValue().stream().map(x -> x.getValue().longValue()).collect(Collectors.toList());
      List<String> tokens =
          ((DynamicArray<Uint256>) params.get(1))
              .getValue().stream()
                  .map(x -> Numeric.toHexStringWithPrefix(x.getValue()))
                  .collect(Collectors.toList());
    }
    

这里需要注意的就是，对于 topic，我们可以通过下标一个个解析。而对于 data，则需要先统一解析出来，再依次解析。

---

*Originally published on [liubo](https://paragraph.com/@liubo/web3j-eth-log)*
