diff --git a/modules/app-cli/dap_app_cli_net.c b/modules/app-cli/dap_app_cli_net.c
index a472241c029a3b5d19c2547def93fd2cb00a5dac..161aa4008ed006d01fdea1f0b43dcfa024488080 100644
--- a/modules/app-cli/dap_app_cli_net.c
+++ b/modules/app-cli/dap_app_cli_net.c
@@ -95,6 +95,16 @@ static void dap_app_cli_http_read(dap_app_cli_connect_param_t *socket, dap_app_c
                 size_t l_head_size = l_str_ptr - l_cmd->cmd_res;
                 memmove(l_cmd->cmd_res, l_str_ptr, l_cmd->cmd_res_cur - l_head_size);
                 l_cmd->cmd_res_cur -= l_head_size;
+                // read rest of data
+                if(l_cmd->cmd_res_cur < l_cmd->cmd_res_len) {
+                    l_cmd->cmd_res = DAP_REALLOC(l_cmd->cmd_res, l_cmd->cmd_res_len + 1);
+                    while((l_cmd->cmd_res_len - l_cmd->cmd_res_cur) > 0) {
+                        ssize_t l_recv_len = recv(*socket, &l_cmd->cmd_res[l_cmd->cmd_res_cur], l_cmd->cmd_res_len - l_cmd->cmd_res_cur, 0);
+                        if(l_recv_len <= 0)
+                            break;
+                        l_cmd->cmd_res_cur += l_recv_len;
+                    }
+                }
                 s_status++;
             } else {
                 break;
diff --git a/modules/chain/dap_chain.c b/modules/chain/dap_chain.c
index 79748f4a8e887ad8fdd3cc155003bb2836b5b64f..4d68717b698a12978760201db76551b8cabacb80 100644
--- a/modules/chain/dap_chain.c
+++ b/modules/chain/dap_chain.c
@@ -93,8 +93,35 @@ void dap_chain_deinit(void)
     dap_chain_item_t * l_item = NULL, *l_tmp = NULL;
     pthread_rwlock_wrlock(&s_chain_items_rwlock);
     HASH_ITER(hh, s_chain_items, l_item, l_tmp) {
-          dap_chain_delete(s_chain_items->chain);
-        }
+          dap_chain_delete(l_item->chain);
+    }
+    pthread_rwlock_unlock(&s_chain_items_rwlock);
+}
+
+
+/**
+ * @brief dap_chain_deinit
+ * note: require dap_chain_enum_unlock() after
+ */
+dap_chain_t* dap_chain_enum(void** a_item)
+{
+    // if a_item == 0x1 then first item
+    dap_chain_item_t *l_item_start = (*a_item == 0x1) ? s_chain_items : (dap_chain_item_t*) *a_item;
+    dap_chain_item_t *l_item = NULL;
+    dap_chain_item_t *l_item_tmp = NULL;
+    pthread_rwlock_wrlock(&s_chain_items_rwlock);
+    HASH_ITER(hh, l_item_start, l_item, l_item_tmp) {
+        *a_item = l_item_tmp;
+        return l_item->chain;
+    }
+    return NULL ;
+}
+
+/**
+ * @brief dap_chain_enum_unlock
+ */
+void dap_chain_enum_unlock(void)
+{
     pthread_rwlock_unlock(&s_chain_items_rwlock);
 }
 
diff --git a/modules/chain/include/dap_chain.h b/modules/chain/include/dap_chain.h
index cf6c1492664526add0905720fd802b14a08355b1..63b32a066797caf11e8403c144c2519acd674e1f 100644
--- a/modules/chain/include/dap_chain.h
+++ b/modules/chain/include/dap_chain.h
@@ -159,6 +159,9 @@ typedef struct dap_chain{
 int dap_chain_init(void);
 void dap_chain_deinit(void);
 
+dap_chain_t* dap_chain_enum(void** a_item);
+void dap_chain_enum_unlock(void);
+
 
 dap_chain_t * dap_chain_create(dap_ledger_t* a_ledger,const char * a_chain_net_name, const char * a_chain_name, dap_chain_net_id_t a_chain_net_id, dap_chain_id_t a_chain_id );
 
diff --git a/modules/global-db/dap_chain_global_db_hist.c b/modules/global-db/dap_chain_global_db_hist.c
index 4ac96d14723d9dba34e479c32cba8cfc776cce10..57204af9dbbbcc9a7ea2dc1d04db140760d6ae87 100644
--- a/modules/global-db/dap_chain_global_db_hist.c
+++ b/modules/global-db/dap_chain_global_db_hist.c
@@ -12,7 +12,7 @@
 #include "dap_chain_global_db_hist.h"
 
 #include "uthash.h"
-// for dap_db_history_filter()
+// for dap_db_history()
 typedef struct dap_tx_data{
         dap_chain_hash_fast_t tx_hash;
         char tx_hash_str[70];
@@ -138,7 +138,7 @@ dap_list_t* dap_db_log_pack(dap_global_db_obj_t *a_obj, size_t *a_data_size_out)
 }
 
 
-// for dap_db_history_filter()
+// for dap_db_history()
 static dap_store_obj_t* get_prev_tx(dap_global_db_obj_t *a_objs, dap_tx_data_t *a_tx_data)
 {
     if(!a_objs || !a_tx_data)
@@ -727,7 +727,7 @@ char* dap_db_history_addr(dap_chain_addr_t * a_addr, const char *a_group_mempool
  *
  * return history string
  */
-char* dap_db_history_filter(dap_chain_addr_t * a_addr, const char *a_group_mempool)
+char* dap_db_history(dap_chain_addr_t * a_addr, const char *a_group_mempool)
 {
     dap_string_t *l_str_out = dap_string_new(NULL);
     // load history
diff --git a/modules/global-db/include/dap_chain_global_db.h b/modules/global-db/include/dap_chain_global_db.h
index 76ae0627161424824ce8d44a65eb493be464bcf3..4dc540b2b1c5c25cab95439ab7ee88e11090ec9c 100644
--- a/modules/global-db/include/dap_chain_global_db.h
+++ b/modules/global-db/include/dap_chain_global_db.h
@@ -124,7 +124,7 @@ dap_list_t* dap_db_log_pack(dap_global_db_obj_t *a_obj, size_t *a_data_size_out)
 // Get data according the history log
 //char* dap_db_history_tx(dap_chain_hash_fast_t * a_tx_hash, const char *a_group_mempool);
 //char* dap_db_history_addr(dap_chain_addr_t * a_addr, const char *a_group_mempool);
-//char* dap_db_history_filter(dap_chain_addr_t * a_addr, const char *a_group_mempool);
+//char* dap_db_history(dap_chain_addr_t * a_addr, const char *a_group_mempool);
 
 // Parse data from dap_db_log_pack()
 void* dap_db_log_unpack(const void *a_data, size_t a_data_size, size_t *a_store_obj_count);
diff --git a/modules/net/dap_chain_node_cli.c b/modules/net/dap_chain_node_cli.c
index ff8712aab3b8221f0d37574ddc7e72053d40c89f..5c4f91cf18c62d5bfee855d97d71c73d10af5395 100644
--- a/modules/net/dap_chain_node_cli.c
+++ b/modules/net/dap_chain_node_cli.c
@@ -63,6 +63,7 @@
 #include "dap_list.h"
 #include "dap_chain_node_cli_cmd.h"
 #include "dap_chain_node_client.h"
+#include "dap_chain_node_cli_cmd_tx.h"
 #include "dap_chain_node_cli.h"
 
 //#include "dap_chain_node_cli.h"
@@ -341,7 +342,16 @@ static void* thread_one_client_func(void *args)
                                                     "Content-Length: %d\r\n\r\n"
                                                     "%s",
                         strlen(reply_body), reply_body);
-                /*int ret = */ send(newsockfd, reply_str, strlen(reply_str) ,0);
+                size_t l_reply_step = 32768;
+                size_t l_reply_len = strlen(reply_str);
+                size_t l_reply_rest = l_reply_len;
+                while(l_reply_rest) {
+                    size_t l_send_bytes = min(l_reply_step, l_reply_rest);
+                    int ret = send(newsockfd, reply_str + l_reply_len - l_reply_rest, l_send_bytes, 0);
+                    if(ret<=0)
+                        break;
+                    l_reply_rest-=l_send_bytes;
+                };
                 DAP_DELETE(str_reply);
                 DAP_DELETE(reply_str);
                 DAP_DELETE(reply_body);
@@ -989,6 +999,18 @@ int dap_chain_node_cli_init(dap_config_t * g_config)
     dap_chain_node_cli_cmd_item_create("tx_history", com_tx_history, NULL, "Transaction history (for address or by hash)",
             "tx_history  [-addr <addr> | -w <wallet name> | -tx <tx_hash>] -net <net name> -chain <chain name>\n");
 
+    // Ledger info
+    dap_chain_node_cli_cmd_item_create("ledger", com_ledger, NULL, "Ledger info",
+            "ledger list coins -net <network name>\n"
+            "ledger list coins_cond -net <network name>\n"
+            "ledger list addrs -net <network name>\n"
+            "ledger tx [all | -addr <addr> | -w <wallet name> | -tx <tx_hash>] [-chain <chain name>] -net <network name>\n");
+
+    // Token info
+    dap_chain_node_cli_cmd_item_create("token", com_token, NULL, "Token info",
+            "token list -net <network name>\n"
+            "token tx all name <token name> -net <network name> [-page_start <page>] [-page <page>]\n");
+
     // Log
     dap_chain_node_cli_cmd_item_create ("print_log", com_print_log, NULL, "Print log info",
                 "print_log [ts_after <timestamp >] [limit <line numbers>]\n" );
diff --git a/modules/net/dap_chain_node_cli_cmd_tx.c b/modules/net/dap_chain_node_cli_cmd_tx.c
index 67cc3f80663625ed6ce8a5c3fb227ba05430c2e9..4882751ee5de8ae28b4a16426ca26a0adbb0dc8f 100644
--- a/modules/net/dap_chain_node_cli_cmd_tx.c
+++ b/modules/net/dap_chain_node_cli_cmd_tx.c
@@ -26,15 +26,18 @@
 #include <stddef.h>
 #include <pthread.h>
 
-#include <dap_common.h>
-#include <dap_enc_base58.h>
-#include <dap_strfuncs.h>
-#include <dap_string.h>
-#include <dap_list.h>
-#include <dap_hash.h>
+#include "dap_common.h"
+#include "dap_enc_base58.h"
+#include "dap_strfuncs.h"
+#include "dap_string.h"
+#include "dap_list.h"
+#include "dap_hash.h"
 
+#include "dap_chain_wallet.h"
+#include "dap_chain_datum.h"
+#include "dap_chain_datum_token.h"
 #include "dap_chain_datum_tx_items.h"
-
+#include "dap_chain_node_cli.h"
 #include "dap_chain_node_cli_cmd_tx.h"
 
 #define LOG_TAG "chain_node_cli_cmd_tx"
@@ -683,3 +686,918 @@ char* dap_db_history_addr(dap_chain_addr_t * a_addr, dap_chain_t * a_chain, cons
     char *l_ret_str = l_str_out ? dap_string_free(l_str_out, false) : NULL;
     return l_ret_str;
 }
+
+static char* dap_db_history_token_list(dap_chain_t * a_chain, const char *a_token_name, const char *a_hash_out_type, size_t *a_token_num)
+{
+    dap_string_t *l_str_out = dap_string_new(NULL);
+    *a_token_num  = 0;
+    bool l_tx_hash_found = false;
+    // list all transactions
+    dap_tx_data_t *l_tx_data_hash = NULL;
+    // load transactions
+    size_t l_atom_size = 0;
+    dap_chain_atom_iter_t *l_atom_iter = a_chain->callback_atom_iter_create(a_chain);
+    dap_chain_atom_ptr_t *l_atom = a_chain->callback_atom_iter_get_first(l_atom_iter, &l_atom_size);
+    size_t l_datums_count = 0;
+    dap_chain_datum_t **l_datums = (a_chain->callback_atom_get_datums && l_atom && l_atom_size) ?
+                    a_chain->callback_atom_get_datums(l_atom, l_atom_size, &l_datums_count) : NULL;
+    if(!l_datums) {
+        log_it(L_WARNING, "Not defined callback_atom_get_datums for chain \"%s\"", a_chain->name);
+        return NULL ;
+    }
+    for(size_t l_datum_n = 0; l_datum_n < l_datums_count; l_datum_n++) {
+
+        dap_chain_datum_t *l_datum = l_datums[l_datum_n];
+        if(!l_datum ) {// || l_datum->header.type_id != DAP_CHAIN_DATUM_TX) {
+            // go to next atom
+            //l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter, &l_atom_size);
+            continue;
+        }
+        /*
+            // transaction
+            dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*) l_datum->data;
+            dap_list_t *l_records_out = NULL;
+
+
+        dap_chain_datum_t *l_datum =
+                a_chain->callback_atom_get_datum ?
+                        a_chain->callback_atom_get_datum(l_atom) : (dap_chain_datum_t*) l_atom;
+        if(!l_datum) {
+            // go to next transaction
+            l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter);
+            l_atom_size = a_chain->callback_atom_get_size(l_atom);
+            log_it(L_ERROR, "datum=NULL for atom=0x%x", l_atom);
+            continue;
+        }
+*/
+        char l_time_str[70];
+        // get time of create datum
+        if(dap_time_to_str_rfc822(l_time_str, 71, l_datum->header.ts_create) < 1)
+            l_time_str[0] = '\0';
+        if(l_datum->header.type_id==DAP_CHAIN_DATUM_TOKEN_DECL) {
+            dap_chain_datum_token_t *l_token = (dap_chain_datum_token_t*) l_datum->data;
+            if(!a_token_name || !dap_strcmp(l_token->ticker, a_token_name)) {
+                dap_string_append_printf(l_str_out, "token %s, created: %s\n", l_token->ticker, l_time_str);
+                switch (l_token->type) {
+                // Simple private token decl
+                case DAP_CHAIN_DATUM_TOKEN_TYPE_SIMPLE:
+                    dap_string_append_printf(l_str_out, "  total_supply: %.0llf(%llu), signs: valid/total %02d/%02d \n",
+                            l_token->header_private.total_supply / DATOSHI_LD,
+                            l_token->header_private.total_supply,
+                            l_token->header_private.signs_valid, l_token->header_private.signs_total);
+                    break;
+                case DAP_CHAIN_DATUM_TOKEN_TYPE_PRIVATE_DECL:
+                    dap_string_append_printf(l_str_out, "  tsd_total_size: %llu, flags: 0x%x \n",
+                            l_token->header_private_decl.tsd_total_size,
+                            l_token->header_private_decl.flags);
+                    break;
+                case DAP_CHAIN_DATUM_TOKEN_TYPE_PRIVATE_UPDATE:
+                    dap_string_append_printf(l_str_out, "  tsd_total_size: %llu, padding: 0x%x \n",
+                            l_token->header_private_update.tsd_total_size,
+                            l_token->header_private_update.padding);
+                    break;
+                case DAP_CHAIN_DATUM_TOKEN_TYPE_PUBLIC: {
+                    char *l_addr = dap_chain_addr_to_str(&l_token->header_public.premine_address);
+                    dap_string_append_printf(l_str_out,
+                            " total_supply: %.0llf(%llu), flags: 0x%x\n, premine_supply: %llu, premine_address '%s'\n",
+                            l_token->header_public.total_supply / DATOSHI_LD,
+                            l_token->header_public.total_supply,
+                            l_token->header_public.flags,
+                            l_token->header_public.premine_supply,
+                            l_addr ? l_addr : "-");
+                    DAP_DELETE(l_addr);
+                }
+                    break;
+                default:
+                    dap_string_append_printf(l_str_out, "unknown token type: 0x%x\n", l_token->type);
+                    break;
+
+                }
+                dap_string_append_printf(l_str_out, "\n");
+                (*a_token_num)++;
+            }
+        }
+
+        // go to next transaction
+        //l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter);
+        //l_atom_size = a_chain->callback_atom_get_size(l_atom);
+    }
+
+    a_chain->callback_atom_iter_delete(l_atom_iter);
+    char *l_ret_str = l_str_out ? dap_string_free(l_str_out, false) : NULL;
+    return l_ret_str;
+}
+
+/**
+ * Get data according the history log
+ *
+ * return history string
+ */
+static char* dap_db_history_filter(dap_chain_t * a_chain, const char *a_filter_token_name, const char *a_filtr_addr_base58, const char *a_hash_out_type, long a_datum_start, long a_datum_end, long *a_total_datums)
+{
+    dap_string_t *l_str_out = dap_string_new(NULL);
+
+    bool l_tx_hash_found = false;
+    // list all transactions
+    dap_tx_data_t *l_tx_data_hash = NULL;
+    // load transactions
+    size_t l_atom_size = 0;
+    dap_chain_atom_iter_t *l_atom_iter = a_chain->callback_atom_iter_create(a_chain);
+    dap_chain_atom_ptr_t *l_atom = a_chain->callback_atom_iter_get_first(l_atom_iter, &l_atom_size);
+    size_t l_datum_num = 0, l_token_num = 0, l_emission_num = 0, l_tx_num = 0;
+    size_t l_datum_num_global = a_total_datums ? *a_total_datums : 0;
+    while(l_atom && l_atom_size) {
+    size_t l_datums_count = 0;
+    dap_chain_datum_t **l_datums =
+            (a_chain->callback_atom_get_datums && l_atom && l_atom_size) ?
+                    a_chain->callback_atom_get_datums(l_atom, l_atom_size, &l_datums_count) : NULL;
+    if(!l_datums) {
+        log_it(L_WARNING, "Not defined callback_atom_get_datums for chain \"%s\"", a_chain->name);
+        return NULL ;
+    }
+    for(size_t l_datum_n = 0; l_datum_n < l_datums_count; l_datum_n++) {
+
+        dap_chain_datum_t *l_datum = l_datums[l_datum_n];
+        if(!l_datum) { // || l_datum->header.type_id != DAP_CHAIN_DATUM_TX) {
+            // go to next atom
+            //l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter, &l_atom_size);
+            continue;
+        }
+
+    /*dap_chain_atom_iter_t *l_atom_iter = a_chain->callback_atom_iter_create(a_chain);
+    dap_chain_atom_ptr_t *l_atom = a_chain->callback_atom_iter_get_first(l_atom_iter);
+    size_t l_atom_size = a_chain->callback_atom_get_size(l_atom);
+    size_t l_datum_num = 0, l_token_num = 0, l_emission_num = 0, l_tx_num = 0;
+    while(l_atom && l_atom_size) {
+        dap_chain_datum_t *l_datum =
+                a_chain->callback_atom_get_datum ?
+                        a_chain->callback_atom_get_datum(l_atom) : (dap_chain_datum_t*) l_atom;
+        if(!l_datum) {
+            // go to next transaction
+            l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter);
+            l_atom_size = a_chain->callback_atom_get_size(l_atom);
+            log_it(L_ERROR, "datum=NULL for atom=0x%x", l_atom);
+            continue;
+        }*/
+        char l_time_str[70];
+        // get time of create datum
+        if(dap_time_to_str_rfc822(l_time_str, 71, l_datum->header.ts_create) < 1)
+            l_time_str[0] = '\0';
+        switch (l_datum->header.type_id) {
+
+        // token
+        case DAP_CHAIN_DATUM_TOKEN_DECL: {
+
+            // no token necessary for addr
+            if(a_filtr_addr_base58) {
+                    break;
+            }
+
+            dap_chain_datum_token_t *l_token = (dap_chain_datum_token_t*) l_datum->data;
+            //if(a_datum_start < 0 || (l_datum_num >= a_datum_start && l_datum_num < a_datum_end))
+            // datum out of page
+            if(a_datum_start >= 0 && (l_datum_num+l_datum_num_global < a_datum_start || l_datum_num+l_datum_num_global >= a_datum_end)){
+                l_token_num++;
+                break;
+            }
+            if(!a_filter_token_name || !dap_strcmp(l_token->ticker, a_filter_token_name)) {
+                dap_string_append_printf(l_str_out, "token %s, created: %s\n", l_token->ticker, l_time_str);
+                switch (l_token->type) {
+                // Simple private token decl
+                case DAP_CHAIN_DATUM_TOKEN_TYPE_SIMPLE:
+                    dap_string_append_printf(l_str_out, "  total_supply: %.0llf(%llu), signs: valid/total %02d/%02d \n",
+                            l_token->header_private.total_supply / DATOSHI_LD,
+                            l_token->header_private.total_supply,
+                            l_token->header_private.signs_valid, l_token->header_private.signs_total);
+                    break;
+                case DAP_CHAIN_DATUM_TOKEN_TYPE_PRIVATE_DECL:
+                    dap_string_append_printf(l_str_out, "  tsd_total_size: %llu, flags: 0x%x \n",
+                            l_token->header_private_decl.tsd_total_size,
+                            l_token->header_private_decl.flags);
+                    break;
+                case DAP_CHAIN_DATUM_TOKEN_TYPE_PRIVATE_UPDATE:
+                    dap_string_append_printf(l_str_out, "  tsd_total_size: %llu, padding: 0x%x \n",
+                            l_token->header_private_update.tsd_total_size,
+                            l_token->header_private_update.padding);
+                    break;
+                case DAP_CHAIN_DATUM_TOKEN_TYPE_PUBLIC: {
+                    char *l_addr = dap_chain_addr_to_str(&l_token->header_public.premine_address);
+                    dap_string_append_printf(l_str_out,
+                            " total_supply: %.0llf(%llu), flags: 0x%x\n, premine_supply: %llu, premine_address '%s'\n",
+                            l_token->header_public.total_supply / DATOSHI_LD,
+                            l_token->header_public.total_supply,
+                            l_token->header_public.flags,
+                            l_token->header_public.premine_supply,
+                            l_addr ? l_addr : "-");
+                    DAP_DELETE(l_addr);
+                }
+                    break;
+                default:
+                    dap_string_append_printf(l_str_out, "unknown token type: 0x%x\n", l_token->type);
+                    break;
+
+                }
+                dap_string_append_printf(l_str_out, "\n");
+                l_token_num++;
+            }
+        }
+            break;
+
+            // emission
+        case DAP_CHAIN_DATUM_TOKEN_EMISSION: {
+            // datum out of page
+            if(a_datum_start >= 0 && (l_datum_num+l_datum_num_global < a_datum_start || l_datum_num+l_datum_num_global >= a_datum_end)) {
+                 l_token_num++;
+                 break;
+            }
+            dap_chain_datum_token_emission_t *l_token_em = (dap_chain_datum_token_emission_t*) l_datum->data;
+            if(!a_filter_token_name || !dap_strcmp(l_token_em->hdr.ticker, a_filter_token_name)) {
+                char * l_token_emission_address_str = dap_chain_addr_to_str(&(l_token_em->hdr.address));
+                // filter for addr
+                if(dap_strcmp(a_filtr_addr_base58,l_token_emission_address_str)) {
+                     break;
+                }
+
+                dap_string_append_printf(l_str_out, "emission: %.0llf(%llu) %s, type: %s, version: %d\n",
+                        l_token_em->hdr.value / DATOSHI_LD, l_token_em->hdr.value, l_token_em->hdr.ticker,
+                        c_dap_chain_datum_token_emission_type_str[l_token_em->hdr.type],
+                        l_token_em->hdr.version);
+                dap_string_append_printf(l_str_out, "  to addr: %s\n", l_token_emission_address_str);
+
+                DAP_DELETE(l_token_emission_address_str);
+                switch (l_token_em->hdr.type) {
+                case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_UNDEFINED:
+                    break;
+                case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_AUTH:
+                    dap_string_append_printf(l_str_out, "  signs_count: %d\n", l_token_em->data.type_auth.signs_count);
+                    break;
+                case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_ALGO:
+                    dap_string_append_printf(l_str_out, "  codename: %s\n", l_token_em->data.type_algo.codename);
+                    break;
+                case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_ATOM_OWNER:
+                    dap_string_append_printf(l_str_out, " value_start: %.0llf(%llu), codename: %s\n",
+                            l_token_em->data.type_atom_owner.value_start / DATOSHI_LD,
+                            l_token_em->data.type_atom_owner.value_start,
+                            l_token_em->data.type_atom_owner.value_change_algo_codename);
+                    break;
+                case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_SMART_CONTRACT: {
+                    char *l_addr = dap_chain_addr_to_str(&l_token_em->data.type_presale.addr);
+                    // get time of create datum
+                    if(dap_time_to_str_rfc822(l_time_str, 71, l_token_em->data.type_presale.lock_time) < 1)
+                            l_time_str[0] = '\0';
+                    dap_string_append_printf(l_str_out, "  flags: 0x%x, lock_time: %s\n", l_token_em->data.type_presale.flags, l_time_str);
+                    dap_string_append_printf(l_str_out, "  addr: %s\n", l_addr);
+                    DAP_DELETE(l_addr);
+                }
+                    break;
+                }
+                dap_string_append_printf(l_str_out, "\n");
+                l_emission_num++;
+            }
+        }
+            break;
+
+            // transaction
+        case DAP_CHAIN_DATUM_TX:{
+
+            // datum out of page
+            if(a_datum_start >= 0 && (l_datum_num+l_datum_num_global < a_datum_start || l_datum_num+l_datum_num_global >= a_datum_end)) {
+                l_tx_num++;
+                break;
+            }
+            dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*) l_datum->data;
+
+            // find Token items - present in emit transaction
+            dap_list_t *l_list_tx_token = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_TOKEN, NULL);
+            // find OUT items
+            dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT, NULL);
+
+            dap_tx_data_t *l_tx_data = NULL;
+
+             // calc tx hash
+             dap_chain_hash_fast_t l_tx_hash;
+            dap_hash_fast(l_tx, dap_chain_datum_tx_get_size(l_tx), &l_tx_hash);
+            char *tx_hash_str;
+            char l_tx_hash_str[70];
+            dap_chain_hash_fast_to_str(&l_tx_hash, l_tx_hash_str, 70);
+            if(!dap_strcmp(a_hash_out_type, "hex"))
+                tx_hash_str = dap_strdup(l_tx_hash_str);
+            else
+                tx_hash_str = dap_enc_base58_from_hex_str_to_str(l_tx_hash_str);
+
+            dap_string_append_printf(l_str_out, "transaction: %s hash: %s\n", l_list_tx_token ? "(emit)" : "", tx_hash_str);
+            DAP_DELETE(tx_hash_str);
+
+            dap_list_t *l_list_tmp = l_list_out_items;
+            while(l_list_tmp) {
+                const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_list_tmp->data;
+                // save OUT item l_tx_out - only for first OUT item
+                if(!l_tx_data)
+                {
+                    // save tx hash
+                    l_tx_data = DAP_NEW_Z(dap_tx_data_t);
+                    dap_chain_hash_fast_t l_tx_hash;
+                    dap_hash_fast(l_tx, dap_chain_datum_tx_get_size(l_tx), &l_tx_hash);
+                    memcpy(&l_tx_data->tx_hash, &l_tx_hash, sizeof(dap_chain_hash_fast_t));
+                    memcpy(&l_tx_data->addr, &l_tx_out->addr, sizeof(dap_chain_addr_t));
+                    dap_chain_hash_fast_to_str(&l_tx_data->tx_hash, l_tx_data->tx_hash_str,
+                            sizeof(l_tx_data->tx_hash_str));
+                    l_tx_data->datum = DAP_NEW_SIZE(dap_chain_datum_t, l_atom_size);
+                    memcpy(l_tx_data->datum, l_datum, l_atom_size);
+                    // save token name
+                    if(l_list_tx_token) {
+                        dap_chain_tx_token_t *tk = l_list_tx_token->data;
+                        memcpy(l_tx_data->token_ticker, tk->header.ticker, sizeof(l_tx_data->token_ticker));
+                    }
+                    // take token from prev out item
+                    else {
+
+                        // find IN items
+                        dap_list_t *l_list_in_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN, NULL);
+                        dap_list_t *l_list_tmp_in = l_list_in_items;
+                        // find token_ticker in prev OUT items
+                        while(l_list_tmp_in) {
+                            const dap_chain_tx_in_t *l_tx_in =
+                                    (const dap_chain_tx_in_t*) l_list_tmp_in->data;
+                            dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash;
+
+                            //find prev OUT item
+                            dap_tx_data_t *l_tx_data_prev = NULL;
+                            HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), l_tx_data_prev);
+                            if(l_tx_data_prev != NULL) {
+                                // fill token in l_tx_data from prev transaction
+                                if(l_tx_data) {
+                                    // get token from prev tx
+                                    memcpy(l_tx_data->token_ticker, l_tx_data_prev->token_ticker,
+                                            sizeof(l_tx_data->token_ticker));
+                                    break;
+                                }
+                            }
+                            l_list_tmp_in = dap_list_next(l_list_tmp_in);
+                        }
+                        if(l_list_in_items)
+                            dap_list_free(l_list_in_items);
+                    }
+                    HASH_ADD(hh, l_tx_data_hash, tx_hash, sizeof(dap_chain_hash_fast_t), l_tx_data);
+                }
+                l_list_tmp = dap_list_next(l_list_tmp);
+            }
+            if(l_list_out_items)
+                dap_list_free(l_list_out_items);
+
+            // found a_tx_hash now
+            // transaction time
+            if(l_tx->header.ts_created > 0) {
+                time_t rawtime = (time_t) l_tx->header.ts_created;
+                struct tm l_timeinfo = { 0 };
+                localtime_r(&rawtime, &l_timeinfo);
+                dap_string_append_printf(l_str_out, " %s", asctime(&l_timeinfo));
+            }
+
+            // find all OUT items in transaction
+            l_list_out_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT, NULL);
+            l_list_tmp = l_list_out_items;
+            while(l_list_tmp) {
+                const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_list_tmp->data;
+                dap_tx_data_t *l_tx_data_prev = NULL;
+
+                const char *l_token_str = NULL;
+                if(l_tx_data)
+                    l_token_str = l_tx_data->token_ticker;
+                char *l_dst_to_str =
+                        (l_tx_out) ? dap_chain_addr_to_str(&l_tx_out->addr) :
+                        NULL;
+                dap_string_append_printf(l_str_out, " OUT item %lld %s to %s\n",
+                        l_tx_out->header.value,
+                        dap_strlen(l_token_str) > 0 ? l_token_str : "?",
+                        l_dst_to_str ? l_dst_to_str : "?"
+                                       );
+                DAP_DELETE(l_dst_to_str);
+                l_list_tmp = dap_list_next(l_list_tmp);
+            }
+
+            // find all IN items in transaction
+            dap_list_t *l_list_in_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN, NULL);
+            l_list_tmp = l_list_in_items;
+            // find cur addr in prev OUT items
+            while(l_list_tmp) {
+                const dap_chain_tx_in_t *l_tx_in = (const dap_chain_tx_in_t*) l_list_tmp->data;
+                dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash;
+                char l_tx_hash_str[70];
+                char *tx_hash_base58_str = NULL;
+                if(!dap_hash_fast_is_blank(&tx_prev_hash)) {
+                    tx_hash_base58_str = dap_enc_base58_from_hex_str_to_str(l_tx_data->tx_hash_str);
+                    dap_chain_hash_fast_to_str(&tx_prev_hash, l_tx_hash_str, sizeof(l_tx_hash_str));
+                }
+                else {
+                    strcpy(l_tx_hash_str, "Null");
+                    tx_hash_base58_str = dap_strdup("Null");
+                }
+                if(!dap_strcmp(a_hash_out_type, "hex"))
+                    dap_string_append_printf(l_str_out, " IN item \n  prev tx_hash %s\n", l_tx_hash_str);
+                else
+                    dap_string_append_printf(l_str_out, " IN item \n  prev tx_hash %s\n", tx_hash_base58_str);
+                DAP_DELETE(tx_hash_base58_str);
+
+                //find prev OUT item
+                dap_tx_data_t *l_tx_data_prev = NULL;
+                HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), l_tx_data_prev);
+                if(l_tx_data_prev != NULL) {
+
+                    dap_chain_datum_t *l_datum_prev = get_prev_tx(l_tx_data_prev);
+                    dap_chain_datum_tx_t *l_tx_prev =
+                            l_datum_prev ? (dap_chain_datum_tx_t*) l_datum_prev->data : NULL;
+
+                    // find OUT items in prev datum
+                    dap_list_t *l_list_out_prev_items = dap_chain_datum_tx_items_get(l_tx_prev,
+                            TX_ITEM_TYPE_OUT, NULL);
+                    // find OUT item for IN item;
+                    dap_list_t *l_list_out_prev_item = dap_list_nth(l_list_out_prev_items,
+                            l_tx_in->header.tx_out_prev_idx);
+                    dap_chain_tx_out_t *l_tx_prev_out =
+                            l_list_out_prev_item ?
+                                                   (dap_chain_tx_out_t*) l_list_out_prev_item->data :
+                                                   NULL;
+                    // print value from prev out item
+                    dap_string_append_printf(l_str_out, "  prev OUT item value=%lld",
+                            l_tx_prev_out ? l_tx_prev_out->header.value : 0);
+                }
+                dap_string_append_printf(l_str_out, "\n");
+                l_list_tmp = dap_list_next(l_list_tmp);
+            }
+
+            if(l_list_tx_token)
+                dap_list_free(l_list_tx_token);
+            if(l_list_out_items)
+                dap_list_free(l_list_out_items);
+            if(l_list_in_items)
+                dap_list_free(l_list_in_items);
+            l_tx_hash_found = true;
+            l_tx_num++;
+        }
+            break;
+        default:
+            dap_string_append_printf(l_str_out, "unknown datum type=%d %lld %s to %s\n", l_datum->header.type_id);
+            break;
+        }
+        l_datum_num++;
+    }
+        // go to next transaction
+        l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter, &l_atom_size);
+        //l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter);
+        //l_atom_size = a_chain->callback_atom_get_size(l_atom);
+    }
+    a_chain->callback_atom_iter_delete(l_atom_iter);
+    //total
+    dap_string_append_printf(l_str_out,
+            "---------------\ntokens: %u\nemissions: %u\ntransactions: %u\ntotal datums: %u", l_token_num,
+            l_emission_num, l_tx_num, l_datum_num);
+
+    // return total datums
+    if(a_total_datums)
+        *a_total_datums = l_datum_num;
+    // delete hashes
+    dap_tx_data_t *l_iter_current, *l_item_tmp;
+    HASH_ITER(hh, l_tx_data_hash , l_iter_current, l_item_tmp)
+    {
+        HASH_DEL(l_tx_data_hash, l_iter_current);
+        // delete datum
+        DAP_DELETE(l_iter_current->datum);
+        // delete struct
+        DAP_DELETE(l_iter_current);
+    }
+
+    // if no history
+    if(!l_str_out->len)
+        dap_string_append(l_str_out, "empty");
+    char *l_ret_str = l_str_out ? dap_string_free(l_str_out, false) : NULL;
+    return l_ret_str;
+}
+
+
+
+
+/**
+ * ledger command
+ *
+ */
+int com_ledger(int a_argc, char ** a_argv, void *a_arg_func, char **a_str_reply)
+{
+    enum { CMD_NONE, CMD_LIST, CMD_TX_HISTORY };
+    int arg_index = 1;
+    const char *l_addr_base58 = NULL;
+    const char *l_wallet_name = NULL;
+    const char *l_net_str = NULL;
+    const char *l_chain_str = NULL;
+    const char *l_tx_hash_str = NULL;
+
+    dap_chain_t * l_chain = NULL;
+    dap_chain_net_t * l_net = NULL;
+
+    const char * l_hash_out_type = NULL;
+    dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-H", &l_hash_out_type);
+    if(!l_hash_out_type)
+        l_hash_out_type = "base58";
+    if(dap_strcmp(l_hash_out_type,"hex") && dap_strcmp(l_hash_out_type,"base58")) {
+        dap_chain_node_cli_set_reply_text(a_str_reply, "invalid parameter -H, valid values: -H <hex | base58>");
+        return -1;
+    }
+
+    int l_cmd = CMD_NONE;
+    if (dap_chain_node_cli_find_option_val(a_argv, 1, 2, "list", NULL))
+        l_cmd = CMD_LIST;
+    else if (dap_chain_node_cli_find_option_val(a_argv, 1, 2, "tx", NULL))
+        l_cmd = CMD_TX_HISTORY;
+    // command tx_history
+    if(l_cmd == CMD_TX_HISTORY) {
+        bool l_is_all = dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-all", NULL);
+        dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-addr", &l_addr_base58);
+        dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-w", &l_wallet_name);
+        dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-net", &l_net_str);
+        dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-chain", &l_chain_str);
+        dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-tx", &l_tx_hash_str);
+
+        if(!l_is_all && !l_addr_base58 && !l_wallet_name && !l_tx_hash_str) {
+            dap_chain_node_cli_set_reply_text(a_str_reply, "command requires parameter '-all' or '-addr' or '-w'");
+            return -1;
+        }
+
+        // Select chain network
+        if(!l_net_str) {
+            dap_chain_node_cli_set_reply_text(a_str_reply, "command requires parameter '-net'");
+            return -2;
+        } else {
+            if((l_net = dap_chain_net_by_name(l_net_str)) == NULL) { // Can't find such network
+                dap_chain_node_cli_set_reply_text(a_str_reply,
+                        "command requires parameter '-net' to be valid chain network name");
+                return -3;
+            }
+        }
+        //Select chain emission
+        if(!l_chain_str) { // chain may be null -> then all chain use
+            //dap_chain_node_cli_set_reply_text(a_str_reply, "command requires parameter '-chain'");
+            //return -4;
+        } else {
+            if((l_chain = dap_chain_net_get_chain_by_name(l_net, l_chain_str)) == NULL) { // Can't find such chain
+                dap_chain_node_cli_set_reply_text(a_str_reply,
+                        "command requires parameter '-chain' to be valid chain name in chain net %s",
+                        l_net_str);
+                return -5;
+            }
+        }
+        //char *l_group_mempool = dap_chain_net_get_gdb_group_mempool(l_chain);
+        //const char *l_chain_group = dap_chain_gdb_get_group(l_chain);
+
+        dap_chain_hash_fast_t l_tx_hash;
+        if(l_tx_hash_str) {
+            if(dap_chain_hash_fast_from_str(l_tx_hash_str, &l_tx_hash) < 0) {
+                l_tx_hash_str = NULL;
+                dap_chain_node_cli_set_reply_text(a_str_reply, "tx hash not recognized");
+                return -1;
+            }
+//        char hash_str[99];
+//        dap_chain_hash_fast_to_str(&l_tx_hash, hash_str,99);
+//        int gsdgsd=523;
+        }
+        dap_chain_addr_t *l_addr = NULL;
+        // if need addr
+        if(l_wallet_name || l_addr_base58) {
+            if(l_wallet_name) {
+                const char *c_wallets_path = dap_chain_wallet_get_path(g_config);
+                dap_chain_wallet_t * l_wallet = dap_chain_wallet_open(l_wallet_name, c_wallets_path);
+                if(l_wallet) {
+                    dap_chain_addr_t *l_addr_tmp = (dap_chain_addr_t *) dap_chain_wallet_get_addr(l_wallet,
+                            l_net->pub.id);
+                    l_addr = DAP_NEW_SIZE(dap_chain_addr_t, sizeof(dap_chain_addr_t));
+                    memcpy(l_addr, l_addr_tmp, sizeof(dap_chain_addr_t));
+                    dap_chain_wallet_close(l_wallet);
+                }
+            }
+            if(!l_addr && l_addr_base58) {
+                l_addr = dap_chain_addr_from_str(l_addr_base58);
+            }
+            if(!l_addr && !l_tx_hash_str) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "wallet address not recognized");
+                return -1;
+            }
+        }
+
+        dap_string_t *l_str_ret = dap_string_new(NULL); //char *l_str_ret = NULL;
+        dap_chain_t *l_chain_cur;
+        void *l_chain_tmp = (void*)0x1;
+        int l_num = 0;
+        // only one chain
+        if(l_chain)
+            l_chain_cur = l_chain;
+        // all chain
+        else
+            l_chain_cur = dap_chain_enum(&l_chain_tmp);
+        while(l_chain_cur) {
+            // only selected net
+            if(l_net->pub.id.uint64 == l_chain_cur->net_id.uint64) {
+                // separator between chains
+                if(l_num>0 && !l_chain)
+                    dap_string_append(l_str_ret, "---------------\n");
+
+                char *l_str_out = NULL;
+                dap_string_append_printf(l_str_ret, "chain: %s\n", l_chain_cur->name);
+                if(l_is_all) {
+                    // without filters
+                    l_str_out = dap_db_history_filter(l_chain_cur, NULL, NULL, l_hash_out_type, -1, 0, NULL);
+                    dap_string_append_printf(l_str_ret, "all history:\n%s\n", l_str_out ? l_str_out : " empty");
+                }
+                else {
+                    l_str_out = l_tx_hash_str ?
+                                                dap_db_history_tx(&l_tx_hash, l_chain_cur, l_hash_out_type) :
+                                                dap_db_history_addr(l_addr, l_chain_cur, l_hash_out_type);
+
+                    if(l_tx_hash_str) {
+                        dap_string_append_printf(l_str_ret, "history for tx hash %s:\n%s\n", l_tx_hash_str,
+                                l_str_out ? l_str_out : " empty");
+                    }
+                    else if(l_addr) {
+                        char *l_addr_str = dap_chain_addr_to_str(l_addr);
+                        dap_string_append_printf(l_str_ret, "history for addr %s:\n%s\n", l_addr_str,
+                                l_str_out ? l_str_out : " empty");
+                        DAP_DELETE(l_addr_str);
+                    }
+                }
+                DAP_DELETE(l_str_out);
+                l_num++;
+            }
+            // only one chain use
+            if(l_chain)
+                break;
+            dap_chain_enum_unlock();
+            l_chain_cur = dap_chain_enum(&l_chain_tmp);
+        }
+        // all chain
+        if(!l_chain)
+            dap_chain_enum_unlock();
+        dap_chain_node_cli_set_reply_text(a_str_reply, l_str_ret->str);
+        dap_string_free(l_str_ret, true);
+        return 0;
+    }
+    else{
+        dap_chain_node_cli_set_reply_text(a_str_reply, "command requires parameter 'list' or 'tx' or 'info'");
+        return -1;
+    }
+}
+
+/**
+ * token command
+ *
+ */
+int com_token(int a_argc, char ** a_argv, void *a_arg_func, char **a_str_reply)
+{
+    enum { CMD_NONE, CMD_LIST, CMD_INFO, CMD_TX };
+    int arg_index = 1;
+    //const char *l_addr_base58 = NULL;
+    //const char *l_wallet_name = NULL;
+    const char *l_net_str = NULL;
+    const char *l_chain_str = NULL;
+
+    dap_chain_t * l_chain = NULL;
+    dap_chain_net_t * l_net = NULL;
+
+    const char * l_hash_out_type = NULL;
+    dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-H", &l_hash_out_type);
+    if(!l_hash_out_type)
+        l_hash_out_type = "base58";
+    if(dap_strcmp(l_hash_out_type,"hex") && dap_strcmp(l_hash_out_type,"base58")) {
+        dap_chain_node_cli_set_reply_text(a_str_reply, "invalid parameter -H, valid values: -H <hex | base58>");
+        return -1;
+    }
+
+    //bool l_is_all = dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-all", NULL);
+    //dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-addr", &l_addr_base58);
+    //dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-w", &l_wallet_name);
+    dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-net", &l_net_str);
+    //dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-chain", &l_chain_str);
+    //dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-tx", &l_tx_hash_str);
+
+    // Select chain network
+    if(!l_net_str) {
+        dap_chain_node_cli_set_reply_text(a_str_reply, "command requires parameter '-net'");
+        return -2;
+    } else {
+        if((l_net = dap_chain_net_by_name(l_net_str)) == NULL) { // Can't find such network
+            dap_chain_node_cli_set_reply_text(a_str_reply,
+                    "command requires parameter '-net' to be valid chain network name");
+            return -3;
+        }
+    }
+
+    int l_cmd = CMD_NONE;
+    if (dap_chain_node_cli_find_option_val(a_argv, 1, 2, "list", NULL))
+        l_cmd = CMD_LIST;
+    else if (dap_chain_node_cli_find_option_val(a_argv, 1, 2, "info", NULL))
+        l_cmd = CMD_INFO;
+    else if (dap_chain_node_cli_find_option_val(a_argv, 1, 2, "tx", NULL))
+            l_cmd = CMD_TX;
+    // token list
+    if(l_cmd == CMD_LIST) {
+        dap_string_t *l_str_out = dap_string_new(NULL);
+        size_t l_token_num_total = 0;
+        // get first chain
+        void *l_chain_tmp = (void*)0x1;
+        dap_chain_t *l_chain_cur = dap_chain_enum(&l_chain_tmp);
+        while(l_chain_cur) {
+            // only selected net
+            if(l_net->pub.id.uint64 == l_chain_cur->net_id.uint64) {
+                size_t l_token_num = 0;
+                char *token_list_str = dap_db_history_token_list(l_chain_cur, NULL, l_hash_out_type, &l_token_num);
+                if(token_list_str)
+                    dap_string_append(l_str_out, token_list_str);
+                l_token_num_total += l_token_num;
+            }
+            // next chain
+            dap_chain_enum_unlock();
+            l_chain_cur = dap_chain_enum(&l_chain_tmp);
+        }
+        dap_chain_enum_unlock();
+        //total
+        dap_string_append_printf(l_str_out, "---------------\ntokens: %u\n", l_token_num_total);
+        dap_chain_node_cli_set_reply_text(a_str_reply, l_str_out->str);
+        dap_string_free(l_str_out, true);
+        return 0;
+
+    }
+    // token info
+    else if(l_cmd == CMD_INFO) {
+        const char *l_token_name_str = NULL;
+        dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-name", &l_token_name_str);
+        if(!l_token_name_str) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "command requires parameter '-name' <token name>");
+                return -4;
+            }
+
+            dap_string_t *l_str_out = dap_string_new(NULL);
+            size_t l_token_num_total = 0;
+            // get first chain
+            void *l_chain_tmp = (void*)0x1;
+            dap_chain_t *l_chain_cur = dap_chain_enum(&l_chain_tmp);
+            while(l_chain_cur) {
+                // only selected net
+                if(l_net->pub.id.uint64 == l_chain_cur->net_id.uint64) {
+                    size_t l_token_num = 0;
+                    // filter - token name
+                    char *token_list_str = dap_db_history_token_list(l_chain_cur, l_token_name_str, l_hash_out_type, &l_token_num);
+                    if(token_list_str)
+                        dap_string_append(l_str_out, token_list_str);
+                    l_token_num_total += l_token_num;
+                }
+                // next chain
+                dap_chain_enum_unlock();
+                l_chain_cur = dap_chain_enum(&l_chain_tmp);
+            }
+            dap_chain_enum_unlock();
+            if(!l_token_num_total)
+                dap_string_append_printf(l_str_out, "token '%s' not found\n", l_token_name_str);
+            dap_chain_node_cli_set_reply_text(a_str_reply, l_str_out->str);
+            dap_string_free(l_str_out, true);
+            return 0;
+
+    }
+    // command tx history
+    else if(l_cmd == CMD_TX) {
+
+        enum { SUBCMD_TX_NONE, SUBCMD_TX_ALL, SUBCMD_TX_ADDR };
+        // find subcommand
+        int l_subcmd = CMD_NONE;
+        const char *l_addr_base58_str = NULL;
+        const char *l_wallet_name = NULL;
+        if(dap_chain_node_cli_find_option_val(a_argv, 2, a_argc, "-all", NULL))
+            l_subcmd = SUBCMD_TX_ALL;
+        else if(dap_chain_node_cli_find_option_val(a_argv, 2, a_argc, "-addr", &l_addr_base58_str))
+            l_subcmd = SUBCMD_TX_ADDR;
+        else if(dap_chain_node_cli_find_option_val(a_argv, 2, a_argc, "-wallet", &l_wallet_name))
+            l_subcmd = SUBCMD_TX_ADDR;
+
+        const char *l_token_name_str = NULL;
+        const char *l_page_start_str = NULL;
+        const char *l_page_size_str = NULL;
+        const char *l_page_str = NULL;
+        dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-name", &l_token_name_str);
+        dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-page_start", &l_page_start_str);
+        dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-page_size", &l_page_size_str);
+        dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-page", &l_page_str);
+        if(!l_token_name_str) {
+            dap_chain_node_cli_set_reply_text(a_str_reply, "command requires parameter '-name' <token name>");
+            return -4;
+        }
+        long l_page_start = -1;// not used if =-1
+        long l_page_size = 10;
+        long l_page = 2;
+        long l_cur_datum = 0;
+        if(l_page_start_str)
+            l_page_start = strtol(l_page_start_str, NULL, 10);
+        if(l_page_size_str) {
+            l_page_size = strtol(l_page_size_str, NULL, 10);
+            if(l_page_size < 1)
+                l_page_size = 1;
+        }
+        if(l_page_str) {
+            l_page = strtol(l_page_str, NULL, 10);
+            if(l_page < 1)
+                l_page = 1;
+        }
+
+
+         // tx all
+        if(l_subcmd == SUBCMD_TX_ALL) {
+            dap_string_t *l_str_out = dap_string_new(NULL);
+            // get first chain
+            void *l_chain_tmp = (void*) 0x1;
+            dap_chain_t *l_chain_cur = dap_chain_enum(&l_chain_tmp);
+            while(l_chain_cur) {
+                // only selected net
+                if(l_net->pub.id.uint64 == l_chain_cur->net_id.uint64) {
+                    long l_chain_datum = l_cur_datum;
+                    char *l_datum_list_str = dap_db_history_filter(l_chain_cur, l_token_name_str, NULL,
+                            l_hash_out_type, l_page_start * l_page_size, (l_page_start+l_page)*l_page_size, &l_chain_datum);
+                    if(l_datum_list_str) {
+                        l_cur_datum += l_chain_datum;
+                        dap_string_append_printf(l_str_out, "Chain: %s\n", l_chain_cur->name);
+                        dap_string_append_printf(l_str_out, "%s\n\n", l_datum_list_str);
+                        DAP_DELETE(l_datum_list_str);
+                    }
+                }
+                // next chain
+                dap_chain_enum_unlock();
+                l_chain_cur = dap_chain_enum(&l_chain_tmp);
+            }
+            dap_chain_enum_unlock();
+            dap_chain_node_cli_set_reply_text(a_str_reply, l_str_out->str);
+            dap_string_free(l_str_out, true);
+            return 0;
+        }
+        // tx -addr or tx -wallet
+        else if(l_subcmd == SUBCMD_TX_ADDR) {
+            // parse addr from -addr <addr> or -wallet <wallet>
+            dap_chain_addr_t *l_addr_base58 = NULL;
+            if(l_addr_base58_str) {
+                //l_addr_base58 = dap_strdup(l_addr_base58_str);
+                l_addr_base58 = dap_chain_addr_from_str(l_addr_base58_str);
+            }
+            else if(l_wallet_name) {
+                const char *c_wallets_path = dap_chain_wallet_get_path(g_config);
+                dap_chain_wallet_t * l_wallet = dap_chain_wallet_open(l_wallet_name, c_wallets_path);
+                if(l_wallet) {
+                    dap_chain_addr_t *l_addr_tmp = (dap_chain_addr_t *) dap_chain_wallet_get_addr(l_wallet,
+                            l_net->pub.id);
+                    l_addr_base58 = DAP_NEW_SIZE(dap_chain_addr_t, sizeof(dap_chain_addr_t));
+                    memcpy(l_addr_base58, l_addr_tmp, sizeof(dap_chain_addr_t));
+                    dap_chain_wallet_close(l_wallet);
+                    char *ffl_addr_base58 = dap_chain_addr_to_str(l_addr_base58);
+                    ffl_addr_base58 = 0;
+                }
+                else {
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "wallet '%s' not found", l_wallet_name);
+                    return -2;
+                }
+            }
+            if(!l_addr_base58) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "address not recognized");
+                return -3;
+            }
+
+            dap_string_t *l_str_out = dap_string_new(NULL);
+            // get first chain
+            void *l_chain_tmp = (void*) 0x1;
+            dap_chain_t *l_chain_cur = dap_chain_enum(&l_chain_tmp);
+            while(l_chain_cur) {
+                // only selected net
+                if(l_net->pub.id.uint64 == l_chain_cur->net_id.uint64) {
+                    long l_chain_datum = l_cur_datum;
+                    char *l_datum_list_str = dap_db_history_addr(l_addr_base58, l_chain_cur, l_hash_out_type);
+                    if(l_datum_list_str) {
+                        l_cur_datum += l_chain_datum;
+                        dap_string_append_printf(l_str_out, "Chain: %s\n", l_chain_cur->name);
+                        dap_string_append_printf(l_str_out, "%s\n\n", l_datum_list_str);
+                        DAP_DELETE(l_datum_list_str);
+                    }
+                }
+                // next chain
+                dap_chain_enum_unlock();
+                l_chain_cur = dap_chain_enum(&l_chain_tmp);
+            }
+            dap_chain_enum_unlock();
+            dap_chain_node_cli_set_reply_text(a_str_reply, l_str_out->str);
+            dap_string_free(l_str_out, true);
+            DAP_DELETE(l_addr_base58);
+            return 0;
+
+        }
+        else{
+            dap_chain_node_cli_set_reply_text(a_str_reply, "not found parameter '-all', '-wallet' or '-addr'");
+            return -1;
+        }
+        return 0;
+    }
+
+    dap_chain_node_cli_set_reply_text(a_str_reply, "unknown command code %d", l_cmd);
+    return -5;
+}
+
+
diff --git a/modules/net/include/dap_chain_node_cli_cmd_tx.h b/modules/net/include/dap_chain_node_cli_cmd_tx.h
index 64fb1fbc0c12fe905efac4c8185075945b73a13d..a7a1a4a317cd9bd34e74e437e3d28771e03c0499 100644
--- a/modules/net/include/dap_chain_node_cli_cmd_tx.h
+++ b/modules/net/include/dap_chain_node_cli_cmd_tx.h
@@ -31,3 +31,16 @@
  */
 char* dap_db_history_tx(dap_chain_hash_fast_t* a_tx_hash, dap_chain_t * a_chain, const char *a_hash_out_type);
 char* dap_db_history_addr(dap_chain_addr_t * a_addr, dap_chain_t * a_chain, const char *a_hash_out_type);
+
+/**
+ * ledger command
+ *
+ */
+int com_ledger(int a_argc, char ** a_argv, void *a_arg_func, char **a_str_reply);
+
+/**
+ * token command
+ *
+ */
+int com_token(int a_argc, char ** a_argv, void *a_arg_func, char **a_str_reply);
+