# 交易所 FAQ

# 常用信息

# TOP Token 相关

# 1. TOP Token

TOP 里最小单位为:微(uTOP)

  • 1 TOP = 10 ^ 6 微(uTOP)
  • 1 分(cTOP):Cent-top = 1/100 TOP, 1/hundred
  • 1 毫(mTOP):Milli-top = 1/1000 TOP, 1/Thousand
  • 1 微(μTOP):Micro-TOP = 1/10^6 TOP, 1/Million

# 2. TOP Token 总发行量

TOP Token 的总发行量为 200亿。

# 账户、交易相关

# 3. 用户账户(地址)和 Table

  • TOP 采用的是 Account 模型。

  • TOP 账户地址以 T80000 开头,格式为 T80000 + 以太坊格式地址。

  • 任一个账户地址通过计算 Hash 逻辑上映射到 64 个 table 中的固定一个,每个 Table 类似一条独立的子链(也是一个逻辑分片)。

  • 每个 table 会对所管辖的账户的交易进行打包并独立出块。

  • 64 个 Table 子链地址对应 Ta0000@0 ~ Ta0000@63。

  • 64 个 Table 子链序号(ID)为 0 ~ 63。

# 4. 账户地址格式

package org.topnetwork.account;

import java.io.IOException;

import org.topnetwork.account.property.AccountUtils;

import com.alibaba.fastjson.JSONObject;

/**
* 构建离线 account
*/
public class CreateOfflineAccount {

public static void main(String[] args) throws IOException {
  // 1.随机创建离线地址
  Account account=new Account();
  System.out.println("account > "+ JSONObject.toJSONString(account));
  System.out.println("privateKeyBytes > "+ account.getPrivateKeyBytes());

  // 2.指定 table 创建离线地址,targetTableId[0,63]
  int targetTableId = 0;
  Account account1 = AccountUtils.genAccount(targetTableId);
  System.out.println("account1 > "+ JSONObject.toJSONString(account1));

  // 3.根据私钥构建地址
  String privateKey = "0x638785b5e9bb9271f031f6ef852e3d5f33b9f46bff6d920b8622d44e69d6666f";
  Account account2 = new Account("0x638785b5e9bb9271f031f6ef852e3d5f33b9f46bff6d920b8622d44e69d6666f");
  System.out.println("account2 > "+ JSONObject.toJSONString(account2));

  // 4. 根据 privateKeyBytes 构建帐户
  byte[] privateKeyBytes = account2.getPrivateKeyBytes();
  Account account3 = new Account(account2.getPrivateKeyBytes());
  System.out.println("account3 >" + JSONObject.toJSONString(account3));
}
}

# 5. 如何扫描某个账户下所有交易

您可以先 查询用户账户所在的 Table 子链地址以及最新块高,再 扫描指定 Table 子链下的所有块

# 6. 如何获取用户地址下的余额

package org.topnetwork.account;

import java.io.IOException;

import org.topnetwork.core.Topj;
import org.topnetwork.methods.response.AccountInfoResponse;
import org.topnetwork.methods.response.ResponseBase;
import org.topnetwork.procotol.http.HttpService;
import org.topnetwork.tx.PollingTransactionReceiptProcessor;

/**
 * 获取指定帐户余额
 */
public class GetAccountBalance {
    private static Topj topj =null;

    public static void main(String[] args) throws IOException {
        HttpService httpService = new HttpService("http://206.189.210.106:19081");
        topj = Topj.build(httpService);
        // 请求失败每 5s 循环调用,最多 3 次 默认 topj.setTransactionReceiptProcessor(new NoOpProcessor());
        topj.setTransactionReceiptProcessor(new PollingTransactionReceiptProcessor(5000, 3));
        // 查询指定地址的帐户余额
        String address = "T80000f4d41bf4035e972649a8168ba06a15fa19a15ded";
        Account account=new Account();
        account.setAddress(address);
        topj.passport(account);
        ResponseBase<AccountInfoResponse> accountResult = topj.getAccount(account);
        System.out.println("account balance (utop) > "+accountResult.getData().getBalance());
    }

}

# 7. 如何获取用户账户下的最新 nonce

package org.topnetwork.account;

import java.io.IOException;

import org.topnetwork.core.Topj;
import org.topnetwork.methods.response.AccountInfoResponse;
import org.topnetwork.methods.response.ResponseBase;
import org.topnetwork.procotol.http.HttpService;
import org.topnetwork.tx.PollingTransactionReceiptProcessor;

/**
 * 获取指定帐户最新 nonce
 */
public class GetAccountNonce {
    private static Topj topj =null;

    public static void main(String[] args) throws IOException {
        HttpService httpService = new HttpService("http://206.189.210.106:19081");
        topj = Topj.build(httpService);
        // 请求失败每 5s 循环调用,最多 3 次 默认 topj.setTransactionReceiptProcessor(new NoOpProcessor());
        topj.setTransactionReceiptProcessor(new PollingTransactionReceiptProcessor(5000, 3));

        // 查询指定地址的帐户信息
        String address = "T80000f4d41bf4035e972649a8168ba06a15fa19a15ded";
        Account account=new Account();
        account.setAddress(address);
        topj.passport(account);
        ResponseBase<AccountInfoResponse> accountResult = topj.getAccount(account);
        System.out.println("account latest nonce > "+accountResult.getData().getNonce());
    }
}

# 8. 如何通过交易 Hash 查询对应的 Table 块

Self 交易为发送和接受均为自己的地址,所以 self 交易只有一个块,而非 self 交易每笔交易有 3 个块。

  • send block 对应发送交易的块。

  • recv block 对应接收交易的块。

  • confirm block 对应确认交易的块。

package org.topnetwork.block;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.topnetwork.account.Account;
import org.topnetwork.core.Topj;
import org.topnetwork.methods.response.ResponseBase;
import org.topnetwork.methods.response.block.TableBlockResponse;
import org.topnetwork.methods.response.tx.TxConsensusState;
import org.topnetwork.methods.response.tx.XTransactionResponse;
import org.topnetwork.procotol.http.HttpService;
import org.topnetwork.tx.PollingTransactionReceiptProcessor;

import java.io.IOException;

/**
 * 如何通过交易 Hash 查询对应的 Table 块
 */
public class GetTableBlockByTxHash {
    private static Topj topj =null;

    public static void main(String[] args) throws IOException {
        HttpService httpService = new HttpService("http://206.189.210.106:19081");
        topj = Topj.build(httpService);
        // 请求失败每 5s 循环调用,最多 3 次 默认 topj.setTransactionReceiptProcessor(new NoOpProcessor());
        topj.setTransactionReceiptProcessor(new PollingTransactionReceiptProcessor(5000, 3));

        Account account=new Account();
        topj.passport(account);

        // 正常发送交易每笔交易会有 3 个块
        String txHash = "0x052945e6f8f56bd9215e603aecde92a255ada57bd3d40cf662fc802805be406a";
        ResponseBase<XTransactionResponse> transactionResponseResponseBase = topj.getTransaction(account,txHash);
        System.out.println("transaction >> " + JSON.toJSONString(transactionResponseResponseBase));
        TxConsensusState state = transactionResponseResponseBase.getData().getTxConsensusState();
        // 1.send block
        ResponseBase<TableBlockResponse> sendTableIdBlock = topj.getTableBlockByHeight(account,state.getSendBlockInfo().getAccount(),state.getSendBlockInfo().getHeight().intValue());
        System.out.println("sendTableIdBlock info > "+ JSONObject.toJSONString(sendTableIdBlock));

        // 2.recv block
        ResponseBase<TableBlockResponse> recvTableIdBlock = topj.getTableBlockByHeight(account,state.getSendBlockInfo().getAccount(),state.getRecvBlockInfo().getHeight().intValue());
        System.out.println("recvTableIdBlock info > "+ JSONObject.toJSONString(recvTableIdBlock));

        // 3.confirm block
        ResponseBase<TableBlockResponse> confirmTableIdBlock = topj.getTableBlockByHeight(account,state.getSendBlockInfo().getAccount(),state.getConfirmBlockInfo().getHeight().intValue());
        System.out.println("confirmTableIdBlock info > "+ JSONObject.toJSONString(confirmTableIdBlock));
    }

}

# 9. 如何发送交易,以及如何在交易里携带 Memo 信息

package org.topnetwork.transaction;

import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Map;

import org.topnetwork.account.Account;
import org.topnetwork.core.Topj;
import org.topnetwork.methods.Model.TransferParams;
import org.topnetwork.methods.request.Transfer;
import org.topnetwork.methods.response.AccountInfoResponse;
import org.topnetwork.methods.response.ResponseBase;
import org.topnetwork.methods.response.XTransaction;
import org.topnetwork.methods.response.tx.XTransactionResponse;
import org.topnetwork.procotol.http.HttpService;
import org.topnetwork.utils.ArgsUtils;

/**
 *  连接rpc直接发送交易
 */
public class SendTransaction {
    // 根据节点地址和端口,初始化通信协议对象,支持websocket
    private static HttpService httpService = new HttpService("http://206.189.210.106:19081");
    private static Topj topj = Topj.build(httpService);

    public static void main(String[] args) throws IOException {
        //1. 构建发送帐户
        Account sendAccount=new Account("0x638785b5e9bb9271f031f6ef852e3d5f33b9f46bff6d920b8622d44e69d6666f");
        topj.passport(sendAccount);
        ResponseBase<AccountInfoResponse> accountResult = topj.getAccount(sendAccount);
        System.out.println("当前账户 nonce > "+accountResult.getData().getBalance());
        System.out.println("当前账户 nonce > "+accountResult.getData().getNonce());
        // 2. 接收者地址
        Account toAccount = new Account();

        // 3. 构建发送交易参数,签名信息
        Transfer transfer = new Transfer();
        TransferParams transferParams = new TransferParams(toAccount.getAddress(), BigInteger.valueOf(1000000), "your note");

        // 发送交易方式一:
        Map<String, String> requestTokenArgsMap = transfer.getArgs(sendAccount, Arrays.asList(transferParams));
        System.out.println("交易体 > "+requestTokenArgsMap);
        // 4.发送交易
        ResponseBase<XTransactionResponse> result = httpService.send(requestTokenArgsMap, XTransactionResponse.class);
        XTransaction xTransaction = ArgsUtils.decodeXTransFromArgs(requestTokenArgsMap);
        XTransactionResponse xTransactionResponse = new XTransactionResponse();
        xTransactionResponse.setOriginalTxInfo(xTransaction);
        result.setData(xTransactionResponse);
        System.out.println(result.getData().getOriginalTxInfo().getTxHash());

        // 发送交易方式二:
        ResponseBase<XTransactionResponse> result2 = topj.transfer(sendAccount,transferParams);
        System.out.println(result2.getData().getOriginalTxInfo().getTxHash());
    }

}

# 10. 如何计算交易手续费

交易手续费 = used_depositsend_block_info) + used_depositconfirm_block_info) + tx_fee。其中:

  • used_deposit 是作为手续费而被扣除的 TOP,当免费的 Tgas 充足时,该值为 0。
  • 对于普通交易,扣除的是 send_block_info 下的 used_deposit ;对于合约交易,扣除的是 confirm_block_info 下的 used_deposit,详见下方代码示例。
  • tx_fee 是一部分系统合约交易的固定手续费(例如在注册节点时会扣除 100 TOP 手续费),对于普通转账交易,该值为 0。
{
    "data": {
        "original_tx_info": {
            "amount": 0,
            "authorization": "0x00da74315ede21da0ebc0ce4f8d1fb8409c978c74a0be4d7660410e3eae7ebcfa36f386bff302c3fd6c7c1be040076fe7f5cda50afc7f842aa71bf05d1a6feea02",
            "edge_nodeid": "",
            "ext": "",
            "last_tx_nonce": 0,
            "note": "",
            "premium_price": 0,
            "receiver_account": "T800002276a7d58218ac4978733e5cca927a7d86cb7c87",
            "receiver_action_name": "",
            "receiver_action_param": "0x2e00000054383030303033376434666263303862663435313361363861323837656432313862306164626434393765663330",
            "send_timestamp": 1631791128,
            "sender_account": "T8000037d4fbc08bf4513a68a287ed218b0adbd497ef30",
            "sender_action_name": "",
            "sender_action_param": "",
            "token_name": "",
            "tx_deposit": 100000,
            "tx_expire_duration": 100,
            "tx_hash": "0xfb33b056757f7d3ba6bccd0c8cd1a923a68dec1fd0c0633f513cee58214b648d",
            "tx_len": 189,
            "tx_structure_version": 2,
            "tx_type": 0
        },
        "tx_consensus_state": {
            "confirm_block_info": {
                "account": "Ta0000@39",
                "exec_status": "success",
                "height": 7,
                "recv_tx_exec_status": "success",
                "used_deposit": 0,
                "used_gas": 0
            },
            "recv_block_info": {
                "account": "Ta0000@55",
                "height": 7,
                "used_gas": 0
            },
            "send_block_info": {
                "account": "Ta0000@39",
                "height": 4,
                "tx_fee": 0,
                "used_deposit": 0,
                "used_gas": 468
            }
        },
        "tx_state" : "success"
    },
    "errmsg": "ok",
    "errno": 0,
    "sequence_id": "17"
}

# 11. (高级用法)如何让创建的所有用户地址,都分布在同一个 Table 子链下

package org.topnetwork.account;

import java.io.IOException;

import org.topnetwork.account.property.AccountUtils;

import com.alibaba.fastjson.JSONObject;

/**
 * 构建离线 account
 */
public class CreateOfflineAccount {

    public static void main(String[] args) throws IOException {
        
        // 指定 table 创建离线地址,targetTableId[0,63]
        int targetTableId = 0;
        Account account1 = AccountUtils.genAccount(targetTableId);
        System.out.println("account1 > "+ JSONObject.toJSONString(account1));
        
    }
}

# Table 链相关

# 12. 查询用户账户所在的 Table 子链地址以及最新块高

package org.topnetwork.account;

import java.io.IOException;

import org.topnetwork.account.property.AccountUtils;
import org.topnetwork.core.Topj;
import org.topnetwork.methods.response.AccountInfoResponse;
import org.topnetwork.methods.response.ResponseBase;
import org.topnetwork.methods.response.block.TableBlockResponse;
import org.topnetwork.procotol.http.HttpService;
import org.topnetwork.tx.PollingTransactionReceiptProcessor;

import com.alibaba.fastjson.JSONObject;

/**
 * 查询用户账号所在的 Table 子链地址,以及最新块高
 */
public class CheckAccount {
    private static Topj topj =null;

    public static void main(String[] args) throws IOException {
        HttpService httpService = new HttpService("http://206.189.210.106:19081");
        topj = Topj.build(httpService);
        // 请求失败每 5s 循环调用,最多 3 次 默认 topj.setTransactionReceiptProcessor(new NoOpProcessor());
        topj.setTransactionReceiptProcessor(new PollingTransactionReceiptProcessor(5000, 3));
     	// 离线获取指定地址对应的 table 子链 id[0,63]
        int tableId = AccountUtils.getAddressTableId(account2.getAddress());
        System.out.println("tableId::"+tableId);

        // 查询帐户最新块高
        ResponseBase<TableBlockResponse> lastTableBlock = topj.getLastTableBlock(account2,account2.getAddress());
        System.out.println("Last Block info > "+ lastTableBlock.getData().getValue().getTableHeight());
    }

}

# 13. 查询指定 Table 子链下指定块高

package org.topnetwork.block;

import java.io.IOException;

import org.topnetwork.account.Account;
import org.topnetwork.account.property.AccountUtils;
import org.topnetwork.core.Topj;
import org.topnetwork.methods.response.ResponseBase;
import org.topnetwork.methods.response.block.TableBlockResponse;
import org.topnetwork.procotol.http.HttpService;
import org.topnetwork.tx.PollingTransactionReceiptProcessor;

import com.alibaba.fastjson.JSONObject;

/**
 * 查询指定帐户所属子链的块信息
 */
public class GetTableBlockByHeight {
    private static Topj topj =null;

    public static void main(String[] args) throws IOException {
        HttpService httpService = new HttpService("http://206.189.210.106:19081");
        topj = Topj.build(httpService);
        // 请求失败每 5s 循环调用,最多 3 次 默认 topj.setTransactionReceiptProcessor(new NoOpProcessor());
        topj.setTransactionReceiptProcessor(new PollingTransactionReceiptProcessor(5000, 3));

        int height = 67;// 指定块高
        String address = "T80000f4d41bf4035e972649a8168ba06a15fa19a15ded";//指定地址
        Account account=new Account();
        account.setAddress(address);
        topj.passport(account);
        // 指定帐户所在的子链地址
        String targetTableAccount = AccountUtils.getAddressTable(account.getAddress());
        // 查询指定 Table 子链下指定块高的块信息
        ResponseBase<TableBlockResponse> subTableIdBlock = topj.getTableBlockByHeight(account, targetTableAccount,height);
        System.out.println("Block info > "+ JSONObject.toJSONString(subTableIdBlock));
    }

}

# 14. 扫描指定 Table 子链下的最新块

package org.topnetwork.block;

import java.io.IOException;

import org.topnetwork.account.Account;
import org.topnetwork.account.property.AccountUtils;
import org.topnetwork.core.Topj;
import org.topnetwork.methods.response.ResponseBase;
import org.topnetwork.methods.response.block.TableBlockResponse;
import org.topnetwork.procotol.http.HttpService;
import org.topnetwork.tx.PollingTransactionReceiptProcessor;

import com.alibaba.fastjson.JSONObject;

/**
 * 查询指定 Table 子链最新的块信息
 */
public class GetTableBlockByTableId {
    private static Topj topj =null;

    public static void main(String[] args) throws IOException {
        HttpService httpService = new HttpService("http://206.189.210.106:19081");
        topj = Topj.build(httpService);
        // 请求失败每 5s 循环调用,最多 3 次 默认 topj.setTransactionReceiptProcessor(new NoOpProcessor());
        topj.setTransactionReceiptProcessor(new PollingTransactionReceiptProcessor(5000, 3));

        int height = 67;// 指定块高
        String address = "T80000f4d41bf4035e972649a8168ba06a15fa19a15ded";//指定地址
        Account account=new Account();
        account.setAddress(address);
        topj.passport(account);

        // 指定帐户所在的子链地址
        String targetTableAccount = AccountUtils.getAddressTable(account.getAddress());
        // 查询指定 Table 子链最新的块信息
        ResponseBase<TableBlockResponse> subTableIdBlock = topj.getLastTableBlock(account, targetTableAccount);
        System.out.println("Block info > "+ JSONObject.toJSONString(subTableIdBlock));
    }

}

# 15. 如何扫描所有的 Table 下指定高度的块

遍历 Ta0000@0 ~ Ta0000@63,扫描每一个 Table 子链中高度为 0 的块。

package org.topnetwork.block;

import java.io.IOException;

import org.topnetwork.account.Account;
import org.topnetwork.account.property.AccountUtils;
import org.topnetwork.core.Topj;
import org.topnetwork.methods.response.ResponseBase;
import org.topnetwork.methods.response.block.TableBlockResponse;
import org.topnetwork.procotol.http.HttpService;
import org.topnetwork.tx.PollingTransactionReceiptProcessor;
import org.topnetwork.utils.TopjConfig;

import com.alibaba.fastjson.JSONObject;

/**
 * 扫描所有子链的块
 */
public class ScanAllTableBlock {

    private static Topj topj =null;

    public static void main(String[] args) throws IOException {
        HttpService httpService = new HttpService("http://206.189.210.106:19081");
        topj = Topj.build(httpService);
        // 请求失败每 5s 循环调用,最多 3 次 默认 topj.setTransactionReceiptProcessor(new NoOpProcessor());
        topj.setTransactionReceiptProcessor(new PollingTransactionReceiptProcessor(5000, 3));
        Account account=new Account();
        topj.passport(account);
        // 扫描指定子链帐户下的块信息,height 从 0 开始 ,
        // 扫描所有的 tabe 子链 [0-63],可以选择每个子链开启线程 height 从 0 开始扫描,最终到 latest height
        int height = 0;
        for (int i=0;i<64;i++){
            // 子链地址
            String targetTableAddress = TopjConfig.getShardingTableBlockAddr() + "@" + i;
            ResponseBase<TableBlockResponse> subTableIdBlock = topj.getTableBlockByHeight(account, targetTableAddress,height);
            System.out.println("Block info > table" +i +" >"+ JSONObject.toJSONString(subTableIdBlock));
        }
    }

}

# 16. 如何查询所有 Table 子链的最新高度

package org.topnetwork.block;

import java.io.IOException;
import java.math.BigInteger;
import java.util.List;

import org.topnetwork.account.Account;
import org.topnetwork.core.Topj;
import org.topnetwork.methods.response.ResponseBase;
import org.topnetwork.procotol.http.HttpService;
import org.topnetwork.tx.PollingTransactionReceiptProcessor;

import com.alibaba.fastjson.JSON;

/**
 * 如何查询所有 Table 子链的最新高度
 */
public class GetAllLastTableBlockHeight {
    private static Topj topj =null;

    public static void main(String[] args) throws IOException {
        HttpService httpService = new HttpService("http://206.189.210.106:19081");
        topj = Topj.build(httpService);
        topj.setTransactionReceiptProcessor(new PollingTransactionReceiptProcessor(5000, 3));
        // 构建随机帐户
        Account firstAccount = new Account();
        // 设置 identityToken
        topj.passport(firstAccount);
        // 获取 64 个 table 的最新高度
        ResponseBase<List<BigInteger>> latestTables = topj.getLatestTables(firstAccount);
        System.out.println("latestTables info > " + JSON.toJSONString(latestTables.getData()));

    }

}

# 浏览器相关

# 17. 如何从 TOP 浏览器查询一个账户信息

点击此处 (opens new window) 进入 TOPscan 查询账户信息。

# 18. 如何从 TOP 浏览器查询一笔交易

点击此处 (opens new window) 进入 TOPscan 查询交易。