Skip to main content

Week15 - 10.17

事件订阅

java-sdk目前支持了事件订阅,那么如何去订阅对应的事件呢?对应的例子奉上~

    public static void pubsub() throws ConnectException {
        WebSocketService wsService = new WebSocketService("wss://test.confluxrpc.com/ws", false);
        wsService.connect();
        Cfx cfx = Cfx.create(wsService);

        // add the filter
        BigInteger cur = cfx.getEpochNumber().sendAndGet();
        // construct the filter parameter
        LogFilter filter = new LogFilter();

        // filter details
//        filter.setFromEpoch(Epoch.numberOf(cur));
//        filter.setToEpoch(Epoch.numberOf(cur.add(new BigInteger("20"))));
//        // To filter address
//        List<Address> toFilterAddress = new ArrayList<Address>();
//        toFilterAddress.add(new Address("cfxtest:aajb342mw5kzad6pjjkdz0wxx0tr54nfwpbu6yaj49"));
//        filter.setAddress(toFilterAddress);

        // subscribe epoch events
//             Flowable<EpochNotification> events1 = cfx.subscribeEpochs();
//             Disposable disposable1 = events1.subscribe(event -> {
//                // You can get the detail through getters
//                  System.out.println(event.getParams().getResult().getEpochNumber());
//                 System.out.println("epoch");
//            });
//
//            disposable1.dispose();

        // subscribe newheads events
        cfx.subscribeNewHeads().subscribe(event -> {
            // You can get the detail through getters
            System.out.println(event.getParams().getResult().getEpochNumber());
        }, error -> {
            error.printStackTrace();
        });

        // subscribe log events
//        cfx.subscribeLogs(filter).subscribe(event -> {
//            // You can get the detail through getters
//            System.out.println(event.getParams().getResult().getLogIndex());
//        }, error -> {
//            error.printStackTrace();
//        });
    }

批量转账

最新的conflux-java sdk基于web3j的batch request类,支持了Batch RPC。本次的tip给出了对应的例程

    public static void test() throws Exception{
        Cfx t = Cfx.create("https://test.confluxrpc.com");
        Web3j client = Web3j.build(new HttpService("https://test.confluxrpc.com"));
        Account acc = Account.create(t, "fjkaldsdjfasxjvzlkxjczxlkjfas"); //replace with your private key or to export the account from the keystore.
        Account.Option option = new Account.Option();
        RawTransaction tx = option.buildTx(t, new Address("cfxtest:aajb342mw5kzad6pjjkdz0wxx0tr54nfwpbu6yaj49"), acc.getPoolNonce(), new Address("cfxtest:aar9up0wsbgtw7f0g5tyc4hbwb2wa5wf7emmk94znd"), null);
        RawTransaction tx1 = option.buildTx(t, new Address("cfxtest:aajb342mw5kzad6pjjkdz0wxx0tr54nfwpbu6yaj49"), acc.getPoolNonce().add(BigInteger.ONE), new Address("cfxtest:aar9up0wsbgtw7f0g5tyc4hbwb2wa5wf7emmk94znd"), null);
        RawTransaction tx2 = option.buildTx(t, new Address("cfxtest:aajb342mw5kzad6pjjkdz0wxx0tr54nfwpbu6yaj49"), acc.getPoolNonce().add(BigInteger.TWO), new Address("cfxtest:aar9up0wsbgtw7f0g5tyc4hbwb2wa5wf7emmk94znd"), null);
        RawTransaction tx3 = option.buildTx(t, new Address("cfxtest:aajb342mw5kzad6pjjkdz0wxx0tr54nfwpbu6yaj49"), acc.getPoolNonce().add(BigInteger.valueOf(3)), new Address("cfxtest:aar9up0wsbgtw7f0g5tyc4hbwb2wa5wf7emmk94znd"), null);

        tx.setValue(BigInteger.valueOf(100));
        String signedTx = acc.sign(tx);
        String signedTx1 = acc.sign(tx1);
        String signedTx2 = acc.sign(tx2);
        String signedTx3 = acc.sign(tx3);

        BatchResponse resp = client.newBatch()
                .add(t.sendRawTransaction(signedTx))
                .add(t.sendRawTransaction(signedTx1))
                .add(t.sendRawTransaction(signedTx2))
                .add(t.sendRawTransaction(signedTx3))
                .send();
        
        System.out.println(resp.getResponses().get(0).getResult());
    }

主要的思路为:

创建web3j的client -> 创建转账交易的rawtransaction -> 将交易进行签名 -> 将签名交易放入web3jclient的batch队列中 -> 发送交易

在交易执行成功后,可以通过一个迭代器,去获取每一笔交易的哈希值。 需要注意的是,目前的java-sdk中需要手动对nonce进行增加,而这一点会在后续的开发当中予以优化。

批量调用合约

批量调用合约于转账交易的思路是一样的,也是创建web3j的client -> 创建转账交易的rawtransaction -> 将交易进行签名 -> 将签名交易放入web3jclient的batch队列中 -> 发送交易

以下给出的例子以ERC20为例:

    public static void batchTx() throws Exception {
        String addr = "cfxtest:acffj2hwbrwbsxuk56jne9913xvmwj5g4u7syhbfr2";
        Cfx cfx = Cfx.create("https://test.confluxrpc.com");
        Web3j client = Web3j.build(new HttpService("https://test.confluxrpc.com"));
        Account acc = Account.create(cfx, "fjkaldsdjfasxjvzlkxjczxlkjfas"); //replace with your own private key.
        BigInteger amount = BigInteger.valueOf(100);
        String data = call(new Address(addr), "transfer", new Address("cfxtest:aar9up0wsbgtw7f0g5tyc4hbwb2wa5wf7emmk94znd").getABIAddress(), new Uint256(amount));

        Account.Option option = new Account.Option();
        RawTransaction tx = option.buildTx(cfx, new Address("cfxtest:aajb342mw5kzad6pjjkdz0wxx0tr54nfwpbu6yaj49"), acc.getPoolNonce(), new Address(addr), data);
        RawTransaction tx1 = option.buildTx(cfx, new Address("cfxtest:aajb342mw5kzad6pjjkdz0wxx0tr54nfwpbu6yaj49"), acc.getPoolNonce().add(BigInteger.ONE), new Address(addr), data);
        RawTransaction tx2 = option.buildTx(cfx, new Address("cfxtest:aajb342mw5kzad6pjjkdz0wxx0tr54nfwpbu6yaj49"), acc.getPoolNonce().add(BigInteger.TWO), new Address(addr), data);

        String signedTx = acc.sign(tx);
        String signedTx1 = acc.sign(tx1);
        String signedTx2 = acc.sign(tx2);


        BatchResponse resp = client.newBatch()
        .add(cfx.sendRawTransaction(signedTx))
        .add(cfx.sendRawTransaction(signedTx1))
        .add(cfx.sendRawTransaction(signedTx2))
        .send();

        System.out.println(resp.getResponses().get(0).getResult());

        }

    public static String call(String method, Type<?>... inputs) throws Exception {

            Function function = new Function(method, Arrays.asList(inputs), Collections.emptyList());
            String data = FunctionEncoder.encode(function);


        return data ;
    }

查询链上信息&查询合约功能

查询方法不需要去构建交易,只需要去创建call方法的request,就能将其放入web3j的batch队列中。在得到查询结果的rawdata后,需要去调用DecodeUtil.decode方法去进行解析。

具体的有:

    public static void queryInfo() throws Exception{
        Cfx t = Cfx.create("https://test.confluxrpc.com");
        Web3j client = Web3j.build(new HttpService("https://test.confluxrpc.com"));

        BatchResponse resp = client.newBatch()
                .add(t.getBestBlockHash())
                .add(t.getBalance(new Address("cfxtest:aajb342mw5kzad6pjjkdz0wxx0tr54nfwpbu6yaj49")))
                .add(t.getEpochNumber())
                .send();

        System.out.println(Numeric.decodeQuantity(resp.getResponses().get(2).getResult().toString()));
        System.out.println(resp.getResponses().get(0).getResult().toString());
        System.out.println(Numeric.decodeQuantity(resp.getResponses().get(1).getResult().toString()));
    }

// 以ERC20为例
    public static void batchQueryContract() throws Exception {
        String addr = "cfxtest:acffj2hwbrwbsxuk56jne9913xvmwj5g4u7syhbfr2";
        Cfx cfx = Cfx.create("https://test.confluxrpc.com");
        Web3j client = Web3j.build(new HttpService("https://test.confluxrpc.com"));
        ContractCall call = new ContractCall(cfx, new Address(addr));


        ContractCall call1 = new ContractCall(cfx, new Address(addr));
        ContractCall call2 = new ContractCall(cfx, new Address(addr));
        ContractCall call3 = new ContractCall(cfx, new Address(addr));
        
        BatchResponse resp = client.newBatch()
                .add(call.call("name"))
                .add(call1.call("symbol"))
                .add(call2.call("totalSupply"))
                .add(call3.call("decimals"))
                .send();
        String name = DecodeUtil.decode(resp.getResponses().get(0).getResult().toString(), Utf8String.class);
        String symbol = DecodeUtil.decode(resp.getResponses().get(1).getResult().toString(), Utf8String.class);
        BigInteger decimals = DecodeUtil.decode(resp.getResponses().get(3).getResult().toString(), Uint8.class);
        BigInteger totalSupply = DecodeUtil.decode(resp.getResponses().get(2).getResult().toString(), Uint256.class);

        System.out.println(name);
        System.out.println(symbol);
        System.out.println(decimals);
        System.out.println(totalSupply);
        }

ERC4907

简介

ERC4907是ERC721的扩展,能够完全向下兼容ERC721。本质上来说,该提案是一个租赁合约。实现了NFT所有权和使用权的分离

动机

在某些场景下,NFT的使用者和所有者并不是相同的,如,在游戏中的租赁系统。ERC4907就是脱胎于这个需求,实现了所有权和使用权的分离。

这种设计模式已经在某些项目当中被使用,ERC4907提出了一个正式的标准,去进行规范。

解决的问题

ERC4907的提出解决了以下三个问题

权利的分离

通过Userowner的角色管理,实现不同角色对NFT的权利的分离。对owner来说,通过ERC4907,它可以设置该NFT的用户,且其他项目也能够将自己的权利分配给owneruser

链上所有权时限管理

通过ERC4907,user对某个NFT的所有权将随着expire时间的到来而自动失去,而不用像原来一样,再发送一笔交易

租赁流程

  1. A 签署租赁合约可以转让A 拥有的NFT。

  2. A 将想出租的NFT 清单发送到租赁合约上。

  3. B 选择一个租赁时间,租金根据租赁时间和租金价格计算。B 转移代币作为租金,租赁合约将NFT 从A 转移到租赁合约地址上,并将NFT 的用户设置为B,设置到期时间为租赁时间。

  4. 当租约到期时,A 可以从租约中赎回NFT。

接口实现

interface IERC4907 {

    // Logged when the user of an NFT is changed or expires is changed
    /// @notice Emitted when the `user` of an NFT or the `expires` of the `user` is changed
    /// The zero address for user indicates that there is no user address
    event UpdateUser(uint256 indexed tokenId, address indexed user, uint64 expires);

    /// @notice set the user and expires of an NFT
    /// @dev The zero address indicates there is no user
    /// Throws if `tokenId` is not valid NFT
    /// @param user  The new user of the NFT
    /// @param expires  UNIX timestamp, The new user could use the NFT before expires
    function setUser(uint256 tokenId, address user, uint64 expires) external;

    /// @notice Get the user address of an NFT
    /// @dev The zero address indicates that there is no user or the user is expired
    /// @param tokenId The NFT to get the user address for
    /// @return The user address for this NFT
    function userOf(uint256 tokenId) external view returns(address);

    /// @notice Get the user expires of an NFT
    /// @dev The zero value indicates that there is no user
    /// @param tokenId The NFT to get the user expires for
    /// @return The user expires for this NFT
    function userExpires(uint256 tokenId) external view returns(uint256);
}

The userOf(uint256 tokenId) function MAY be implemented as pure or view.

The userExpires(uint256 tokenId) function MAY be implemented as pure or view.

The setUser(uint256 tokenId, address user, uint64 expires) function MAY be implemented as public or external.

The UpdateUser event MUST be emitted when a user address is changed or the user expires is changed.

The supportsInterface method MUST return true when called with 0xad092b5c.

例程

EIP-4907例子