From fdbc71ced6f5f1048cd0d7e9e6a2580b5ad75e5e Mon Sep 17 00:00:00 2001
From: Roman Khlopkov <roman.khlopkov@demlabs.net>
Date: Thu, 31 Mar 2022 16:08:12 +0000
Subject: [PATCH] features-5879

---
 dap-sdk/core/src/dap_strfuncs.c               |  11 +-
 dap-sdk/crypto/include/dap_enc_base58.h       |   1 -
 dap-sdk/crypto/include/dap_hash.h             |   5 +-
 dap-sdk/crypto/include/dap_uuid.h             |   3 +-
 dap-sdk/crypto/src/dap_enc_base58.c           |  28 --
 dap-sdk/crypto/src/dap_enc_msrln.c            |  28 +-
 dap-sdk/crypto/src/dap_hash.c                 |  41 +-
 dap-sdk/crypto/src/dap_sign.c                 |   2 +-
 dap-sdk/crypto/src/dap_uuid.c                 |  13 +
 dap-sdk/net/server/enc_server/dap_enc_http.c  |   2 +-
 .../http_server/http_client/dap_http_client.c |   2 +-
 .../http_client/dap_http_user_agent.c         |  74 +--
 modules/chain/dap_chain_ledger.c              |   5 +-
 modules/common/dap_chain_datum_token.c        |  70 ++-
 .../common/include/dap_chain_datum_token.h    |  33 +-
 modules/global-db/dap_chain_global_db.c       |   6 +-
 .../global-db/dap_chain_global_db_remote.c    |   4 +-
 .../include/dap_chain_global_db_driver.h      |   4 +-
 modules/mempool/dap_chain_mempool.c           |  82 +++-
 modules/mempool/include/dap_chain_mempool.h   |   7 +
 modules/net/dap_chain_node_cli.c              |   5 +-
 modules/net/dap_chain_node_cli_cmd.c          | 451 ++++++++----------
 modules/net/dap_chain_node_cli_cmd_tx.c       |   6 +-
 modules/type/blocks/dap_chain_cs_blocks.c     |   4 +-
 24 files changed, 494 insertions(+), 393 deletions(-)

diff --git a/dap-sdk/core/src/dap_strfuncs.c b/dap-sdk/core/src/dap_strfuncs.c
index db6107fe15..509c7ba6c0 100755
--- a/dap-sdk/core/src/dap_strfuncs.c
+++ b/dap-sdk/core/src/dap_strfuncs.c
@@ -696,14 +696,13 @@ size_t dap_str_countv(char **a_str_array)
 
 size_t dap_str_symbol_count(const char *a_str, char a_sym)
 {
-   const char *p = a_str;
-   uint32_t l_count = 0;
+    const char *p = a_str;
+    uint32_t l_count = 0;
 
-   while(*p){
-        if(p++ == a_sym) 
+    while (*p)
+        if (*p++ == a_sym)
             l_count++;
-   }
-   return l_count;
+    return l_count;
 }
 
 /**
diff --git a/dap-sdk/crypto/include/dap_enc_base58.h b/dap-sdk/crypto/include/dap_enc_base58.h
index 8652d7bfd0..8392fb617e 100755
--- a/dap-sdk/crypto/include/dap_enc_base58.h
+++ b/dap-sdk/crypto/include/dap_enc_base58.h
@@ -47,7 +47,6 @@ char* dap_enc_base58_encode_hash_to_str(dap_chain_hash_fast_t *a_in_hash);
 char* dap_enc_base58_from_hex_str_to_str(const char *a_in_str);
 // convert from "Bura1HFrKsqbdytEXQVrxpbovtvLhR1VbrJs65JBx3gc" to "0xA21F1E865B6740A28E8708798ECF25D2C0AA596DF5EB1FD724186B6AD7FF2199"
 char* dap_enc_base58_to_hex_str_from_str(const char *a_in_str);
-int dap_enc_base58_hex_to_hash(const char * a_hex_str,  dap_chain_hash_fast_t * a_datum_hash);
 
 #ifdef __cplusplus
 }
diff --git a/dap-sdk/crypto/include/dap_hash.h b/dap-sdk/crypto/include/dap_hash.h
index 73b0c3a286..0a1f997aaf 100755
--- a/dap-sdk/crypto/include/dap_hash.h
+++ b/dap-sdk/crypto/include/dap_hash.h
@@ -53,8 +53,9 @@ typedef dap_chain_hash_fast_t dap_hash_fast_t;
 extern "C" {
 #endif
 
-int dap_chain_hash_fast_from_str( const char * a_hash_str, dap_hash_fast_t * a_hash);
-
+int dap_chain_hash_fast_from_str( const char * a_hash_str, dap_hash_fast_t *a_hash);
+int dap_chain_hash_fast_from_hex_str( const char *a_hex_str, dap_chain_hash_fast_t *a_hash);
+int dap_chain_hash_fast_from_base58_str(const char *a_base58_str,  dap_chain_hash_fast_t *a_hash);
 /**
  * @brief 
  * get SHA3_256 hash for specific data
diff --git a/dap-sdk/crypto/include/dap_uuid.h b/dap-sdk/crypto/include/dap_uuid.h
index e6cd754ae1..6aa5e7e2bf 100644
--- a/dap-sdk/crypto/include/dap_uuid.h
+++ b/dap-sdk/crypto/include/dap_uuid.h
@@ -26,4 +26,5 @@
 
 uint128_t dap_uuid_generate_uint128(); // Produce uint128 unique id
 uint64_t dap_uuid_generate_uint64(); // Produce uint64 unique id
-
+// Produces unique nonce
+void dap_uuid_generate_nonce(void *a_nonce, size_t a_nonce_size);
diff --git a/dap-sdk/crypto/src/dap_enc_base58.c b/dap-sdk/crypto/src/dap_enc_base58.c
index 0614fa39ef..12054863ab 100755
--- a/dap-sdk/crypto/src/dap_enc_base58.c
+++ b/dap-sdk/crypto/src/dap_enc_base58.c
@@ -226,34 +226,6 @@ char* dap_enc_base58_encode_hash_to_str(dap_chain_hash_fast_t *a_in_hash)
     return dap_enc_base58_encode_to_str(a_in_hash->raw, sizeof(dap_chain_hash_fast_t));
 }
 
-/**
- * @brief dap_enc_base58_hex_to_hash
- * @param a_hex_str
- * @param a_datum_hash
- * @return
- */
-int dap_enc_base58_hex_to_hash(const char * a_hex_str,  dap_chain_hash_fast_t * a_datum_hash)
-{
-    assert(a_datum_hash);
-
-    if (a_hex_str){
-        char* l_datum_base58 = dap_enc_base58_from_hex_str_to_str(a_hex_str);
-        void * l_decoded = DAP_NEW_Z_SIZE(void,strlen(l_datum_base58));
-        size_t l_decoded_size;
-        if (( l_decoded_size = dap_enc_base58_decode(l_datum_base58, l_decoded))!= sizeof (*a_datum_hash) ){
-            memcpy( a_datum_hash, l_decoded, l_decoded_size);
-            return 0;
-        }else{
-            log_it(L_ERROR,"Wrong hash format: can't parse \"%s\", decoded size %zd when expected %zd", a_hex_str, l_decoded_size,
-                                                                                                       sizeof (*a_datum_hash));
-            return -1;
-        }
-
-    }else
-        return -1;
-}
-
-
 // convert from "0xA21F1E865B6740A28E8708798ECF25D2C0AA596DF5EB1FD724186B6AD7FF2199" to "Bura1HFrKsqbdytEXQVrxpbovtvLhR1VbrJs65JBx3gc"
 char* dap_enc_base58_from_hex_str_to_str(const char *a_in_str)
 {
diff --git a/dap-sdk/crypto/src/dap_enc_msrln.c b/dap-sdk/crypto/src/dap_enc_msrln.c
index 4933d37800..be01acd0b9 100755
--- a/dap-sdk/crypto/src/dap_enc_msrln.c
+++ b/dap-sdk/crypto/src/dap_enc_msrln.c
@@ -93,28 +93,30 @@ void dap_enc_msrln_key_generate(struct dap_enc_key * a_key, const void *kex_buf,
 size_t dap_enc_msrln_gen_bob_shared_key(struct dap_enc_key* b_key, const void* a_pub, size_t a_pub_size, void ** b_pub)
 {
     size_t ret;
-
     uint8_t *bob_tmp_pub = NULL;
 
+    if(*b_pub) {
+        DAP_DELETE(*b_pub);
+    }
+    *b_pub = NULL;
     if(b_key->priv_key_data_size == 0) { // need allocate memory for priv key
         b_key->priv_key_data = malloc(MSRLN_SHAREDKEY_BYTES);
         b_key->priv_key_data_size = MSRLN_SHAREDKEY_BYTES;
     }
- //   b_key->priv_key_data = NULL;
+
+    if(b_key->priv_key_data == NULL) {
+        return 0;
+    }
 
     if(a_pub_size != MSRLN_PKA_BYTES) {
         ret = 0;
-        if(*b_pub) {
-            DAP_DELETE(b_pub);
-        }
-        b_pub = NULL;
         DAP_DELETE(b_key->priv_key_data);
         b_key->priv_key_data = NULL;
         return ret;
     }
 
     *b_pub = malloc(MSRLN_PKB_BYTES);
-    if(b_pub == NULL) {
+    if(*b_pub == NULL) {
         ret = 0;
         DAP_DELETE(b_key->priv_key_data);
         b_key->priv_key_data = NULL;
@@ -122,20 +124,12 @@ size_t dap_enc_msrln_gen_bob_shared_key(struct dap_enc_key* b_key, const void* a
     }
     bob_tmp_pub = *b_pub;
 
-//    b_key->priv_key_data = malloc(MSRLN_SHAREDKEY_BYTES);
-    if(b_key->priv_key_data == NULL) {
-        ret = 0;
-        DAP_DELETE(b_pub);
-        b_pub = NULL;
-        return ret;
-    }
-
     PLatticeCryptoStruct PLCS = LatticeCrypto_allocate();
     LatticeCrypto_initialize(PLCS, (RandomBytes)randombytes, MSRLN_generate_a, MSRLN_get_error);
     if (MSRLN_SecretAgreement_B((unsigned char *) a_pub, (unsigned char *) b_key->priv_key_data, (unsigned char *) bob_tmp_pub, PLCS) != CRYPTO_MSRLN_SUCCESS) {
         ret = 0;
-        DAP_DELETE(b_pub);
-        b_pub = NULL;
+        DAP_DELETE(*b_pub);
+        *b_pub = NULL;
         DAP_DELETE(b_key->priv_key_data);
         b_key->priv_key_data = NULL;
         return ret;
diff --git a/dap-sdk/crypto/src/dap_hash.c b/dap-sdk/crypto/src/dap_hash.c
index 7a62574684..dd37423851 100755
--- a/dap-sdk/crypto/src/dap_hash.c
+++ b/dap-sdk/crypto/src/dap_hash.c
@@ -23,8 +23,10 @@
 */
 #include <stdio.h>
 #include <stdlib.h>
+#include "dap_strfuncs.h"
 #include "dap_common.h"
 #include "dap_hash.h"
+#include "dap_enc_base58.h"
 
 #include "KeccakHash.h"
 #include "SimpleFIPS202.h"
@@ -37,17 +39,17 @@
  * @param a_hash
  * @return
  */
-int dap_chain_hash_fast_from_str( const char * a_hash_str, dap_chain_hash_fast_t * a_hash)
+int dap_chain_hash_fast_from_hex_str( const char *a_hex_str, dap_chain_hash_fast_t *a_hash)
 {
     const size_t c_hash_str_size = sizeof(*a_hash) * 2 + 1 /*trailing zero*/+ 2 /* heading 0x */;
-    size_t l_hash_str_len = strlen( a_hash_str);
+    size_t l_hash_str_len = strlen(a_hex_str);
     if ( l_hash_str_len + 1 == c_hash_str_size ){
         for(size_t l_offset = 2; l_offset < l_hash_str_len; l_offset += 2) {
             char l_byte;
-            if(dap_sscanf(a_hash_str + l_offset, "%02hhx", &l_byte) != 1) {
-                if(dap_sscanf(a_hash_str + l_offset, "%02hhx", &l_byte) != 1) {
+            if(dap_sscanf(a_hex_str + l_offset, "%02hhx", &l_byte) != 1) {
+                if(dap_sscanf(a_hex_str + l_offset, "%02hhx", &l_byte) != 1) {
                     log_it(L_ERROR, "dap_chain_str_to_hash_fast parse error: offset=%zu, hash_str_len=%zu, str=\"%2s\"",
-                            l_offset, l_hash_str_len, a_hash_str + l_offset);
+                            l_offset, l_hash_str_len, a_hex_str + l_offset);
                     return -10 * ((int) l_offset); // Wrong char
                 }
             }
@@ -58,3 +60,32 @@ int dap_chain_hash_fast_from_str( const char * a_hash_str, dap_chain_hash_fast_t
         return -1;
 }
 
+
+/**
+ * @brief dap_chain_hash_fast_from_base58_str
+ * @param a_base58_str
+ * @param a_datum_hash
+ * @return
+ */
+int dap_chain_hash_fast_from_base58_str(const char *a_base58_str,  dap_chain_hash_fast_t *a_hash)
+{
+    if (!a_hash)
+        return -1;
+    if (!a_base58_str)
+        return -2;
+    size_t l_hash_len = dap_strlen(a_base58_str);
+    if (l_hash_len > DAP_ENC_BASE58_ENCODE_SIZE(sizeof(dap_hash_fast_t)))
+        return -3;
+    // from base58 to binary
+    byte_t l_out[DAP_ENC_BASE58_DECODE_SIZE(DAP_ENC_BASE58_ENCODE_SIZE(sizeof(dap_hash_fast_t)))];
+    size_t l_out_size = dap_enc_base58_decode(a_base58_str, l_out);
+    if (l_out_size != sizeof(dap_hash_fast_t))
+        return -4;
+    memcpy(a_hash, l_out, sizeof(dap_hash_fast_t));
+    return 0;
+}
+
+int dap_chain_hash_fast_from_str( const char *a_hash_str, dap_chain_hash_fast_t *a_hash)
+{
+    return dap_chain_hash_fast_from_hex_str(a_hash_str, a_hash) && dap_chain_hash_fast_from_base58_str(a_hash_str, a_hash);
+}
diff --git a/dap-sdk/crypto/src/dap_sign.c b/dap-sdk/crypto/src/dap_sign.c
index 628d1e0bf3..0e9b6708d1 100755
--- a/dap-sdk/crypto/src/dap_sign.c
+++ b/dap-sdk/crypto/src/dap_sign.c
@@ -347,7 +347,7 @@ bool dap_sign_verify_size(dap_sign_t *a_sign, size_t a_max_key_size)
         return false;
     if (a_sign->header.sign_pkey_size > a_max_key_size)
         return false;
-    if (a_sign->header.sign_size > a_max_key_size)
+    if (a_sign->header.sign_size == 0 || a_sign->header.sign_size > a_max_key_size)
         return false;
     return true;
 }
diff --git a/dap-sdk/crypto/src/dap_uuid.c b/dap-sdk/crypto/src/dap_uuid.c
index ac4a130496..e7b8408bc7 100644
--- a/dap-sdk/crypto/src/dap_uuid.c
+++ b/dap-sdk/crypto/src/dap_uuid.c
@@ -73,3 +73,16 @@ uint64_t dap_uuid_generate_uint64()
    //         l_input[0],l_input[1],l_input[2],l_input[3]);
     return l_output;
 }
+
+void dap_uuid_generate_nonce(void *a_nonce, size_t a_nonce_size)
+{
+    if (!a_nonce || !a_nonce_size)
+        return;
+    uint32_t l_input[4] ={
+        [0] = random_uint32_t(UINT32_MAX),
+        [1] = time(NULL),
+        [2] = atomic_fetch_add(&s_global_counter, 1),
+        [3] = random_uint32_t(UINT32_MAX)
+    };
+    SHAKE128((unsigned char *)a_nonce, a_nonce_size, (unsigned char *)l_input, sizeof(l_input));
+}
diff --git a/dap-sdk/net/server/enc_server/dap_enc_http.c b/dap-sdk/net/server/enc_server/dap_enc_http.c
index 0257ea5161..df5a6b4ac2 100644
--- a/dap-sdk/net/server/enc_server/dap_enc_http.c
+++ b/dap-sdk/net/server/enc_server/dap_enc_http.c
@@ -114,7 +114,7 @@ void enc_http_proc(struct dap_http_simple *cl_st, void * arg)
             return;
         } else if (l_decode_len > l_pkey_exchange_size+ sizeof (dap_sign_hdr_t)) {
             dap_sign_t *l_sign = (dap_sign_t *)&alice_msg[l_pkey_exchange_size];
-            if (l_sign->header.sign_size == 0 || l_sign->header.sign_size > (l_decode_len - l_pkey_exchange_size)  ){
+            if(!dap_sign_verify_size(l_sign, l_decode_len - l_pkey_exchange_size)) {
                 log_it(L_WARNING,"Wrong signature size %u (decoded length %zu)",l_sign->header.sign_size, l_decode_len);
                 *return_code = Http_Status_BadRequest;
                 return;
diff --git a/dap-sdk/net/server/http_server/http_client/dap_http_client.c b/dap-sdk/net/server/http_server/http_client/dap_http_client.c
index 888052db73..87b9f0a39f 100644
--- a/dap-sdk/net/server/http_server/http_client/dap_http_client.c
+++ b/dap-sdk/net/server/http_server/http_client/dap_http_client.c
@@ -381,7 +381,7 @@ void dap_http_client_read( dap_events_socket_t *a_esocket, void *a_arg )
                         }
 
                         if ( strstr(l_query_string, "HTTP/1.1") ){
-                            strncpy( l_http_client->in_query_string, l_query_string + 1, len_after - 11 );
+                            strncpy( l_http_client->in_query_string, l_query_string + 1, len_after - 8 );
                         }else{
                             strncpy( l_http_client->in_query_string,l_query_string + 1, len_after );
                         }
diff --git a/dap-sdk/net/server/http_server/http_client/dap_http_user_agent.c b/dap-sdk/net/server/http_server/http_client/dap_http_user_agent.c
index 24f6b913cf..4a72ca62fb 100644
--- a/dap-sdk/net/server/http_server/http_client/dap_http_user_agent.c
+++ b/dap-sdk/net/server/http_server/http_client/dap_http_user_agent.c
@@ -1,5 +1,6 @@
 #include "dap_http_user_agent.h"
 #include "dap_common.h"
+#include "dap_strfuncs.h"
 #include <string.h>
 #include <stdio.h>
 
@@ -15,18 +16,16 @@ struct dap_http_user_agent {
 
 static char* _dap_http_user_agent_to_string(dap_http_user_agent_ptr_t a_agent)
 {
-    char * result = calloc(1, sizeof(*a_agent));
-
+    char *l_result;
     if(a_agent->comment) {
-        dap_sprintf(result, "%s/%d.%d %s", a_agent->name,
+        l_result = dap_strdup_printf("%s/%d.%d %s", a_agent->name,
                 a_agent->major_version, a_agent->minor_version,
                 a_agent->comment);
     } else {
-        dap_sprintf(result, "%s/%d.%d", a_agent->name,
+        l_result = dap_strdup_printf("%s/%d.%d", a_agent->name,
                 a_agent->major_version, a_agent->minor_version);
     }
-
-    return result;
+    return l_result;
 }
 
 
@@ -40,28 +39,30 @@ dap_http_user_agent_ptr_t dap_http_user_agent_new(const char* a_name,
         return NULL;
     }
 
-    dap_http_user_agent_ptr_t res = DAP_NEW_Z(struct dap_http_user_agent);
-    res->name = strdup(a_name);
-    res->comment = a_comment ? strdup(a_comment) : NULL;
-    res->major_version = a_major_version;
-    res->minor_version = a_minor_version;
-    res->string_representation = _dap_http_user_agent_to_string(res);
-    return res;
+    dap_http_user_agent_ptr_t l_res = DAP_NEW_Z(struct dap_http_user_agent);
+    l_res->name = dap_strdup(a_name);
+    l_res->comment = dap_strdup(a_comment);
+    l_res->major_version = a_major_version;
+    l_res->minor_version = a_minor_version;
+    l_res->string_representation = _dap_http_user_agent_to_string(l_res);
+    return l_res;
 }
 
 void dap_http_user_agent_delete(dap_http_user_agent_ptr_t a_agent)
 {
-    free(a_agent->name);
-    free(a_agent->comment);
-    free(a_agent->string_representation);
-    free(a_agent);
+    if(a_agent != NULL) {
+        DAP_DELETE(a_agent->name);
+        DAP_DELETE(a_agent->comment);
+        DAP_DELETE(a_agent->string_representation);
+        DAP_DELETE(a_agent);
+    }
 }
 
 dap_http_user_agent_ptr_t dap_http_user_agent_new_from_str(const char* a_user_agent_str)
 {
-    dap_http_user_agent_ptr_t result = NULL;
+    dap_http_user_agent_ptr_t l_result = NULL;
     /* Parse user agent line */
-    char* user_agent_str_copy = strdup(a_user_agent_str);
+    char* user_agent_str_copy = dap_strdup(a_user_agent_str);
     char* version_line = strtok(user_agent_str_copy, " ");
     char* comment = strtok(NULL, " ");
 
@@ -81,27 +82,30 @@ dap_http_user_agent_ptr_t dap_http_user_agent_new_from_str(const char* a_user_ag
     }
     /* PARSE LINE successful */
 
-    result = DAP_NEW_Z(struct dap_http_user_agent);
-    result->name = strdup(l_name);
-    result->comment = comment ? strdup(comment) : NULL;
-    result->major_version = (unsigned int) atoi(l_major);
-    result->minor_version = (unsigned int) atoi(l_minor);
+    l_result = DAP_NEW_Z(struct dap_http_user_agent);
+    l_result->name = dap_strdup(l_name);
+    l_result->comment = dap_strdup(comment);
+    l_result->major_version = (unsigned int) atoi(l_major);
+    l_result->minor_version = (unsigned int) atoi(l_minor);
 
 END:
-    free(user_agent_str_copy);
-    return result;
+    DAP_DELETE(user_agent_str_copy);
+    return l_result;
 }
 
-void dap_http_user_agent_add_comment(dap_http_user_agent_ptr_t a_agent, const char* comment)
+void dap_http_user_agent_add_comment(dap_http_user_agent_ptr_t a_agent, const char *comment)
 {
-    // TODO
+    if(a_agent->comment) {
+        DAP_DELETE(a_agent->comment);
+    }
+    a_agent->comment = dap_strdup(comment);
 }
 
-static inline int _compare_versions(unsigned int ver1, unsigned int ver2)
+static inline int _compare_versions(unsigned int a_ver1, unsigned int a_ver2)
 {
-    if(ver1 > ver2)
+    if(a_ver1 > a_ver2)
         return 1;
-    if(ver1 < ver2)
+    if(a_ver1 < a_ver2)
         return -1;
     return 0;
 }
@@ -109,13 +113,15 @@ static inline int _compare_versions(unsigned int ver1, unsigned int ver2)
 int dap_http_user_agent_versions_compare(dap_http_user_agent_ptr_t a_agent1,
                                          dap_http_user_agent_ptr_t a_agent2)
 {
-    if(strcmp(a_agent1->name, a_agent2->name) != 0) {
+    if(dap_strcmp(a_agent1->name, a_agent2->name) != 0) {
         log_it(L_ERROR, "Names not equal");
         return -3;
     }
 
-    int result = _compare_versions(a_agent1->major_version, a_agent2->major_version);
-    if(result != 0) return result;
+    int l_result = _compare_versions(a_agent1->major_version, a_agent2->major_version);
+    if(l_result != 0) {
+        return l_result;
+    }
     return _compare_versions(a_agent1->minor_version, a_agent2->minor_version);
 }
 
diff --git a/modules/chain/dap_chain_ledger.c b/modules/chain/dap_chain_ledger.c
index 46fe5e9734..541e8968c9 100644
--- a/modules/chain/dap_chain_ledger.c
+++ b/modules/chain/dap_chain_ledger.c
@@ -610,7 +610,7 @@ static int s_token_tsd_parse(dap_ledger_t * a_ledger, dap_chain_ledger_token_ite
                         return -12;
                     }
                     // Check if its already present
-                    if (a_token_item->tx_recv_allow)
+                    if (a_token_item->tx_recv_allow) {
                         for( size_t i=0; i < a_token_item->tx_recv_allow_size; i++){ // Check for all the list
                             if ( memcmp(&a_token_item->tx_recv_allow[i], l_tsd->data, l_tsd->size) == 0 ){ // Found
                                 char * l_addr_str= dap_chain_addr_to_str((dap_chain_addr_t*) l_tsd->data );
@@ -626,6 +626,7 @@ static int s_token_tsd_parse(dap_ledger_t * a_ledger, dap_chain_ledger_token_ite
                         if(a_token_item->tx_recv_allow){
                             memcpy(&a_token_item->tx_recv_allow[a_token_item->tx_recv_allow_size], l_tsd->data,l_tsd->size);
                             a_token_item->tx_recv_allow_size++;
+                        }
 
                     }else{
                         log_it(L_ERROR,"Out of memory! Can't extend TX_RECEIVER_ALLOWED array");
@@ -1444,7 +1445,7 @@ int dap_chain_ledger_token_emission_add_check(dap_ledger_t *a_ledger, byte_t *a_
             pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock);
             if (l_token_item){
                 assert(l_token_item->datum_token);
-                dap_sign_t *l_sign = (dap_sign_t *)l_emission->data.type_auth.signs;
+                dap_sign_t *l_sign = (dap_sign_t *)(l_emission->tsd_n_signs + l_emission->data.type_auth.tsd_total_size);
                 size_t l_offset = (byte_t *)l_sign - (byte_t *)l_emission;
                 uint16_t l_aproves = 0, l_aproves_valid = l_token_item->auth_signs_valid;
                 for (uint16_t i = 0; i < l_emission->data.type_auth.signs_count && l_offset < l_emission_size; i++) {
diff --git a/modules/common/dap_chain_datum_token.c b/modules/common/dap_chain_datum_token.c
index 46a357606d..f45501ccd0 100644
--- a/modules/common/dap_chain_datum_token.c
+++ b/modules/common/dap_chain_datum_token.c
@@ -26,6 +26,7 @@
 #include "dap_strfuncs.h"
 #include "dap_common.h"
 #include "dap_chain_datum_token.h"
+#include "dap_uuid.h"
 
 #define LOG_TAG "dap_chain_datum_token"
 
@@ -257,6 +258,19 @@ err:
 
 }
 
+dap_chain_datum_token_emission_t *dap_chain_datum_emission_create(uint256_t a_value, const char *a_ticker, dap_chain_addr_t *a_addr)
+{
+    dap_chain_datum_token_emission_t *l_emission = DAP_NEW_Z(dap_chain_datum_token_emission_t);
+    l_emission->hdr.version = 2;
+    l_emission->hdr.value_256 = a_value;
+    strncpy(l_emission->hdr.ticker, a_ticker, DAP_CHAIN_TICKER_SIZE_MAX - 1);
+    l_emission->hdr.ticker[DAP_CHAIN_TICKER_SIZE_MAX - 1] = '\0';
+    l_emission->hdr.type = DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_AUTH;
+    memcpy(&l_emission->hdr.address, a_addr, sizeof(l_emission->hdr.address));
+    dap_uuid_generate_nonce(&l_emission->hdr.nonce, DAP_CHAIN_DATUM_NONCE_SIZE);
+    return l_emission;
+}
+
 size_t dap_chain_datum_emission_get_size(uint8_t *a_emission_serial)
 {
     size_t l_ret = 0;
@@ -266,28 +280,11 @@ size_t dap_chain_datum_emission_get_size(uint8_t *a_emission_serial)
     } else {
         l_ret = sizeof(l_emission->hdr);
     }
-    switch (l_emission->hdr.type) {
-        case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_AUTH: {
-            uint16_t l_sign_count = *(uint16_t *)(a_emission_serial + l_ret);
-            l_ret += sizeof(l_emission->data.type_auth);
-            for (uint16_t i = 0; i < l_sign_count; i++) {
-                dap_sign_t *l_sign = (dap_sign_t *)(a_emission_serial + l_ret);
-                l_ret += dap_sign_get_size(l_sign);
-            }
-        } break;
-        case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_ALGO:
-            l_ret += sizeof(l_emission->data.type_algo);
-            break;
-        case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_ATOM_OWNER:
-            l_ret += sizeof(l_emission->data.type_atom_owner);
-            break;
-        case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_SMART_CONTRACT:
-            l_ret += sizeof(l_emission->data.type_presale);
-            break;
-        case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_UNDEFINED:
-        default:
-            break;
+    if (l_emission->hdr.type == DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_AUTH) {
+        uint64_t l_size = *(uint64_t *)(a_emission_serial + l_ret);
+        l_ret += l_size;
     }
+    l_ret += sizeof(l_emission->data);
     return l_ret;
 }
 
@@ -319,6 +316,37 @@ dap_chain_datum_token_emission_t *dap_chain_datum_emission_read(byte_t *a_emissi
     return l_emission;
 }
 
+dap_chain_datum_token_emission_t *dap_chain_datum_emission_add_tsd(dap_chain_datum_token_emission_t *a_emission, int a_type, size_t a_size, void *a_data)
+{
+    if (!a_emission || a_emission->hdr.type != DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_AUTH)
+        return NULL;
+    dap_tsd_t *l_tsd = dap_tsd_create(a_type, a_data, a_size);
+    size_t l_tsd_size = sizeof(dap_tsd_t) + a_size;
+    size_t l_emission_size = dap_chain_datum_emission_get_size((uint8_t *)a_emission);
+    dap_chain_datum_token_emission_t *l_emission = DAP_REALLOC(a_emission, l_emission_size + l_tsd_size);
+    memcpy(l_emission->tsd_n_signs + l_emission->data.type_auth.tsd_total_size, l_tsd, l_tsd_size);
+    DAP_DELETE(l_tsd);
+    l_emission->data.type_auth.tsd_total_size += l_tsd_size;
+    return l_emission;
+}
+
+dap_chain_datum_token_emission_t *dap_chain_datum_emission_add_sign(dap_enc_key_t *a_sign_key, dap_chain_datum_token_emission_t *a_emission)
+{
+    if (!a_emission)
+        return NULL;
+    size_t l_emission_size = dap_chain_datum_emission_get_size((uint8_t *)a_emission);
+    dap_sign_t *l_sign = dap_sign_create(a_sign_key, a_emission, sizeof(a_emission->hdr), 0);
+    if (!l_sign)
+        return NULL;
+    dap_chain_datum_token_emission_t *l_ret = DAP_REALLOC(a_emission, l_emission_size + dap_sign_get_size(l_sign));
+    size_t l_sign_size = dap_sign_get_size(l_sign);
+    memcpy(l_ret->tsd_n_signs + l_ret->data.type_auth.size, l_sign, l_sign_size);
+    DAP_DELETE(l_sign);
+    l_ret->data.type_auth.size += l_sign_size;
+    l_ret->data.type_auth.signs_count++;
+    return l_ret;
+}
+
 // #define DAP_CHAIN_DATUM_TOKEN_TYPE_OLD_SIMPLE           0x0001
 // Extended declaration of privatetoken with in-time control
 // #define DAP_CHAIN_DATUM_TOKEN_TYPE_OLD_PRIVATE_DECL     0x0002
diff --git a/modules/common/include/dap_chain_datum_token.h b/modules/common/include/dap_chain_datum_token.h
index bc791ce7b7..6f48cea89d 100644
--- a/modules/common/include/dap_chain_datum_token.h
+++ b/modules/common/include/dap_chain_datum_token.h
@@ -78,8 +78,6 @@ typedef struct dap_chain_datum_token{
             };
             uint16_t signs_valid; // Emission auth signs
             uint16_t signs_total; // Emission auth signs
-            uint16_t padding01; // Token declaration flags
-            uint64_t padding02; 
         } DAP_ALIGN_PACKED header_simple;
         // Private token declarations, with flags, manipulations and updates
         struct {
@@ -134,7 +132,7 @@ typedef struct dap_chain_datum_token{
             dap_chain_addr_t premine_address;
             uint32_t flags;
         } DAP_ALIGN_PACKED header_public;
-        byte_t free_space[152]; // For future changes
+        byte_t free_space[256]; // For future changes
     };
     byte_t data_n_tsd[]; // Signs and/or types-size-data sections
 } DAP_ALIGN_PACKED dap_chain_datum_token_t;
@@ -407,7 +405,7 @@ typedef struct dap_chain_datum_token_emission{
             uint64_t value;
             uint256_t value_256;
         };
-        uint8_t nonce[DAP_CHAIN_DATUM_NONCE_SIZE];
+        uint8_t nonce[DAP_CHAIN_DATUM_NONCE_SIZE];       
     } DAP_ALIGN_PACKED hdr;
     union {
         struct {
@@ -423,10 +421,13 @@ typedef struct dap_chain_datum_token_emission{
             char codename[32];
         } DAP_ALIGN_PACKED type_algo;
         struct {
+            uint64_t size;
+            uint64_t tsd_total_size;
             uint16_t signs_count;
-            byte_t  signs[];
         } DAP_ALIGN_PACKED type_auth;// Signs if exists
+        byte_t free_space[128];     // For future changes
     } data;
+    byte_t tsd_n_signs[];
 } DAP_ALIGN_PACKED dap_chain_datum_token_emission_t;
 
 // Different emissions type
@@ -435,12 +436,17 @@ typedef struct dap_chain_datum_token_emission{
 #define DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_ALGO              0x02
 #define DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_ATOM_OWNER        0x03
 #define DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_SMART_CONTRACT    0x04
-// 256
-// #define DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_256_UNDEFINED         0x05
-// #define DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_256_AUTH              0x06
-// #define DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_256_ALGO              0x07
-// #define DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_256_ATOM_OWNER        0x08
-// #define DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_256_SMART_CONTRACT    0x09
+
+// TSD sections with emission additional params for AUTH type
+#define DAP_CHAIN_DATUM_EMISSION_TSD_TYPE_UNKNOWN           0x0000
+#define DAP_CHAIN_DATUM_EMISSION_TSD_TYPE_TIMESTAMP         0x0001
+#define DAP_CHAIN_DATUM_EMISSION_TSD_TYPE_ADDRESS           0x0002
+#define DAP_CHAIN_DATUM_EMISSION_TSD_TYPE_VALUE             0x0003
+#define DAP_CHAIN_DATUM_EMISSION_TSD_TYPE_CONTRACT          0x0004
+#define DAP_CHAIN_DATUM_EMISSION_TSD_TYPE_NET_ID            0x0005
+#define DAP_CHAIN_DATUM_EMISSION_TSD_TYPE_BLOCK_NUM         0x0006
+#define DAP_CHAIN_DATUM_EMISSION_TSD_TYPE_TOKEN_SYM         0x0007
+
 extern const char *c_dap_chain_datum_token_emission_type_str[];
 
 /// TDS op funcs
@@ -449,8 +455,11 @@ void dap_chain_datum_token_flags_dump(dap_string_t * a_str_out, uint16_t a_flags
 void dap_chain_datum_token_certs_dump(dap_string_t * a_str_out, byte_t * a_data_n_tsd, size_t a_certs_size);
 dap_sign_t ** dap_chain_datum_token_simple_signs_parse(dap_chain_datum_token_t * a_datum_token, size_t a_datum_token_size, size_t *a_signs_count, size_t * a_signs_valid);
 dap_chain_datum_token_t *dap_chain_datum_token_read(byte_t *a_token_serial, size_t *a_token_size);
+
+dap_chain_datum_token_emission_t *dap_chain_datum_emission_create(uint256_t a_value, const char *a_ticker, dap_chain_addr_t *a_addr);
+dap_chain_datum_token_emission_t *dap_chain_datum_emission_add_tsd(dap_chain_datum_token_emission_t *a_emission, int a_type, size_t a_size, void *a_data);
 dap_chain_datum_token_emission_t *dap_chain_datum_emission_read(byte_t *a_emission_serial, size_t *a_emission_size);
 size_t dap_chain_datum_emission_get_size(uint8_t *a_emission_serial);
-
+dap_chain_datum_token_emission_t *dap_chain_datum_emission_add_sign(dap_enc_key_t *a_sign_key, dap_chain_datum_token_emission_t *a_emission);
 // 256 TYPE
 bool dap_chain_datum_token_is_old(uint8_t a_type);
diff --git a/modules/global-db/dap_chain_global_db.c b/modules/global-db/dap_chain_global_db.c
index 800954aab7..f96f89b1fc 100644
--- a/modules/global-db/dap_chain_global_db.c
+++ b/modules/global-db/dap_chain_global_db.c
@@ -586,8 +586,8 @@ bool dap_chain_global_db_gr_set(const char *a_key, const void *a_value, size_t a
 
     store_data.key = a_key;
     store_data.value_len = (a_value_len == (size_t) -1) ? dap_strlen(a_value) : a_value_len;
-    store_data.value = store_data.value_len ? a_value : NULL;
-    store_data.group = a_group;
+    store_data.value = store_data.value_len ? (void *)a_value : NULL;
+    store_data.group = (char *)a_group;
     store_data.timestamp = time(NULL);
 
     lock();
@@ -599,7 +599,7 @@ bool dap_chain_global_db_gr_set(const char *a_key, const void *a_value, size_t a
         // delete info about the deleted entry from the base if one present
         global_db_gr_del_del( a_key, a_group);
 
-        store_data.value = a_value;
+        store_data.value = (void *)a_value;
         store_data.key = a_key;
 
         dap_global_db_obj_track_history(&store_data);
diff --git a/modules/global-db/dap_chain_global_db_remote.c b/modules/global-db/dap_chain_global_db_remote.c
index 044a8ef9b1..00334ccc05 100644
--- a/modules/global-db/dap_chain_global_db_remote.c
+++ b/modules/global-db/dap_chain_global_db_remote.c
@@ -336,8 +336,8 @@ dap_store_obj_t *dap_store_unpacket_multiple(const dap_store_obj_pkt_t *a_pkt, s
 
         if (l_offset+ l_str_length > a_pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'key' field"); break;} // Check for buffer boundries
         l_obj->key = DAP_NEW_SIZE(char, l_str_length + 1);
-        memcpy(l_obj->key, a_pkt->data + l_offset, l_str_length);
-        l_obj->key[l_str_length] = '\0';
+        memcpy((char *)l_obj->key, a_pkt->data + l_offset, l_str_length);
+        ((char *)l_obj->key)[l_str_length] = '\0';
         l_offset += l_str_length;
 
         if (l_offset+sizeof (uint64_t)> a_pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'value_length' field"); break;} // Check for buffer boundries
diff --git a/modules/global-db/include/dap_chain_global_db_driver.h b/modules/global-db/include/dap_chain_global_db_driver.h
index edbee142d5..7072350136 100644
--- a/modules/global-db/include/dap_chain_global_db_driver.h
+++ b/modules/global-db/include/dap_chain_global_db_driver.h
@@ -41,10 +41,10 @@ typedef struct dap_store_obj {
     uint64_t id;
     uint64_t timestamp;
     uint32_t type;                              /* Operation type: ADD/DELETE, see DAP_DB$K_OPTYPE_* constants */
-    const char *group;
+    char *group;
     const char *key;
     const char *c_key;
-    const uint8_t *value;
+    uint8_t *value;
     uint64_t value_len;
 
     dap_proc_queue_callback_t cb;               /* (Async mode only!) A call back to be called on request completion */
diff --git a/modules/mempool/dap_chain_mempool.c b/modules/mempool/dap_chain_mempool.c
index ee705f2552..4aa40a2f6f 100644
--- a/modules/mempool/dap_chain_mempool.c
+++ b/modules/mempool/dap_chain_mempool.c
@@ -43,9 +43,8 @@
 #include "dap_hash.h"
 #include "dap_http_client.h"
 #include "dap_http_simple.h"
-//#include "dap_enc_http.h"
+#include "dap_enc_base58.h"
 #include "dap_enc_http.h"
-//#include "dap_http.h"
 #include "http_status_code.h"
 #include "dap_chain_common.h"
 #include "dap_chain_node.h"
@@ -93,8 +92,8 @@ char *dap_chain_mempool_datum_add(const dap_chain_datum_t *a_datum, dap_chain_t
     if (dap_chain_global_db_gr_set(l_key_str, a_datum, dap_chain_datum_size(a_datum), l_gdb_group)) {
         log_it(L_NOTICE, "Datum with data's hash %s was placed in mempool", l_key_str);
     } else {
-        log_it(L_WARNING, "Can't place data's hash %s was placed in mempool", l_key_str);
-        DAP_DELETE(l_key_str);
+        log_it(L_WARNING, "Can't place data's hash %s in mempool", l_key_str);
+        DAP_DEL_Z(l_key_str);
     }
 
     DAP_DELETE(l_gdb_group);
@@ -587,6 +586,81 @@ dap_chain_hash_fast_t* dap_chain_mempool_tx_create_cond(dap_chain_net_t * a_net,
     return l_key_hash;
 }
 
+dap_chain_hash_fast_t *dap_chain_mempool_base_tx_create(dap_chain_t *a_chain, dap_chain_hash_fast_t *a_emission_hash,
+                                                        dap_chain_id_t a_emission_chain_id, uint256_t a_emission_value, const char *a_ticker,
+                                                        dap_chain_addr_t *a_addr_to, dap_cert_t **a_certs, size_t a_certs_count)
+{
+    char *l_gdb_group_mempool_base_tx = dap_chain_net_get_gdb_group_mempool(a_chain);
+    // create first transaction (with tx_token)
+    dap_chain_datum_tx_t *l_tx = DAP_NEW_Z_SIZE(dap_chain_datum_tx_t, sizeof(dap_chain_datum_tx_t));
+    l_tx->header.ts_created = time(NULL);
+    dap_chain_hash_fast_t l_tx_prev_hash = { 0 };
+    // create items
+
+    dap_chain_tx_token_t *l_tx_token = dap_chain_datum_tx_item_token_create(a_emission_chain_id, a_emission_hash, a_ticker);
+    dap_chain_tx_in_t *l_in = dap_chain_datum_tx_item_in_create(&l_tx_prev_hash, 0);
+    dap_chain_tx_out_t *l_out = dap_chain_datum_tx_item_out_create(a_addr_to, a_emission_value);
+
+    // pack items to transaction
+    dap_chain_datum_tx_add_item(&l_tx, (const uint8_t*) l_tx_token);
+    dap_chain_datum_tx_add_item(&l_tx, (const uint8_t*) l_in);
+    dap_chain_datum_tx_add_item(&l_tx, (const uint8_t*) l_out);
+
+    if (a_certs) {
+        // Sign all that we have with certs
+        for(size_t i = 0; i < a_certs_count; i++) {
+            if(dap_chain_datum_tx_add_sign_item(&l_tx, a_certs[i]->enc_key) < 0) {
+                log_it(L_WARNING, "No private key for certificate '%s'", a_certs[i]->name);
+                return NULL;
+            }
+        }
+    }
+
+    DAP_DEL_Z(l_tx_token);
+    DAP_DEL_Z(l_in);
+    DAP_DEL_Z(l_out);
+
+    size_t l_tx_size = dap_chain_datum_tx_get_size(l_tx);
+
+    // Pack transaction into the datum
+    dap_chain_datum_t * l_datum_tx = dap_chain_datum_create(DAP_CHAIN_DATUM_TX, l_tx, l_tx_size);
+    size_t l_datum_tx_size = dap_chain_datum_size(l_datum_tx);
+    DAP_DEL_Z(l_tx);
+    // calc datum hash
+    dap_chain_hash_fast_t *l_datum_tx_hash = DAP_NEW(dap_hash_fast_t);
+    dap_hash_fast(l_datum_tx, l_datum_tx_size, l_datum_tx_hash);
+    char *l_tx_hash_str = dap_chain_hash_fast_to_str_new(l_datum_tx_hash);
+    // Add to mempool tx token
+    bool l_placed = dap_chain_global_db_gr_set(l_tx_hash_str, l_datum_tx,
+                                               l_datum_tx_size, l_gdb_group_mempool_base_tx);
+    DAP_DEL_Z(l_tx_hash_str);
+    DAP_DELETE(l_datum_tx);
+    if (!l_placed) {
+        return NULL;
+    }
+    return l_datum_tx_hash;
+}
+
+dap_chain_datum_token_emission_t *dap_chain_mempool_emission_get(dap_chain_t *a_chain, const char *a_emission_hash_str)
+{
+    size_t l_emission_size;
+    char *l_gdb_group = dap_chain_net_get_gdb_group_mempool(a_chain);
+    dap_chain_datum_t *l_emission = (dap_chain_datum_t *)dap_chain_global_db_gr_get(
+                                                    a_emission_hash_str, &l_emission_size, l_gdb_group);
+    if (!l_emission) {
+        char *l_emission_hash_str_from_base58 = dap_enc_base58_to_hex_str_from_str(a_emission_hash_str);
+        l_emission = (dap_chain_datum_t *)dap_chain_global_db_gr_get(
+                                    l_emission_hash_str_from_base58, &l_emission_size, l_gdb_group);
+        DAP_DELETE(l_emission_hash_str_from_base58);
+    }
+    DAP_DELETE(l_gdb_group);
+    if (!l_emission)
+        return NULL;
+    dap_chain_datum_token_emission_t *l_ret = dap_chain_datum_emission_read(l_emission->data, &l_emission_size);
+    DAP_DELETE(l_emission);
+    return l_ret;
+}
+
 uint8_t* dap_datum_mempool_serialize(dap_datum_mempool_t *datum_mempool, size_t *size)
 {
     size_t a_request_size = 2 * sizeof(uint16_t), shift_size = 0;
diff --git a/modules/mempool/include/dap_chain_mempool.h b/modules/mempool/include/dap_chain_mempool.h
index 9cc95a0723..a89a68be4b 100644
--- a/modules/mempool/include/dap_chain_mempool.h
+++ b/modules/mempool/include/dap_chain_mempool.h
@@ -5,6 +5,7 @@
 #include "dap_chain_net.h"
 #include "dap_chain_ledger.h"
 #include "dap_http.h"
+#include "dap_cert.h"
 /*
  // datum mempool structure
  typedef struct dap_datum_mempool {
@@ -65,3 +66,9 @@ int dap_chain_mempool_tx_create_massive(dap_chain_t * a_chain, dap_enc_key_t *a_
         const dap_chain_addr_t* a_addr_fee,
         const char a_token_ticker[DAP_CHAIN_TICKER_SIZE_MAX],
         uint256_t a_value, uint256_t a_value_fee, size_t a_tx_num);
+
+dap_chain_hash_fast_t *dap_chain_mempool_base_tx_create(dap_chain_t *a_chain, dap_chain_hash_fast_t *a_emission_hash,
+                                                        dap_chain_id_t a_emission_chain_id, uint256_t a_emission_value, const char *a_ticker,
+                                                        dap_chain_addr_t *a_addr_to, dap_cert_t **a_certs, size_t a_certs_count);
+dap_chain_datum_token_emission_t *dap_chain_mempool_emission_get(dap_chain_t *a_chain, const char *a_emission_hash_str);
+
diff --git a/modules/net/dap_chain_node_cli.c b/modules/net/dap_chain_node_cli.c
index c8da12bf8b..943ee15f77 100644
--- a/modules/net/dap_chain_node_cli.c
+++ b/modules/net/dap_chain_node_cli.c
@@ -1046,7 +1046,7 @@ int dap_chain_node_cli_init(dap_config_t * g_config)
             );
 
     dap_chain_node_cli_cmd_item_create ("token_emit", com_token_emit, "Token emission",
-            "token_emit -net <net name> -chain_emission <chain for emission> -chain_base_tx <chain for base tx> -addr <addr> -token <token ticker> -certs <cert> -emission_value <val>\n");
+            "token_emit {sign | -token <token ticker> -emission_value <val>} -net <net name> [-chain_emission <chain for emission>] [-chain_base_tx <chain for base tx> -addr <addr>] -certs <cert list>\n");
 
     dap_chain_node_cli_cmd_item_create ("mempool_list", com_mempool_list, "List mempool entries for selected chain network",
             "mempool_list -net <net name>\n");
@@ -1071,7 +1071,8 @@ int dap_chain_node_cli_init(dap_config_t * g_config)
 
     // Transaction commands
     dap_chain_node_cli_cmd_item_create ("tx_create", com_tx_create, "Make transaction",
-            "tx_create -net <net name> -chain <chain name> -from_wallet <name> -to_addr <addr> -token <token ticker> -value <value> [-fee <addr> -value_fee <val>]\n" );
+            "tx_create -net <net name> -chain <chain name> {-from_wallet <name> -token <token ticker> -value <value> -to_addr <addr> | -from_emission <emission_hash>}"
+                                        "[-fee <addr> -value_fee <val>]\n" );
     dap_chain_node_cli_cmd_item_create ("tx_cond_create", com_tx_cond_create, "Make cond transaction",
                                         "tx_cond_create -net <net name> -token <token ticker> -wallet <from wallet> -cert <public cert>"
                                         "-value <value datoshi> -unit <mb | kb | b | sec | day> -srv_uid <numeric uid>\n" );
diff --git a/modules/net/dap_chain_node_cli_cmd.c b/modules/net/dap_chain_node_cli_cmd.c
index 805f3d6084..359901a30b 100644
--- a/modules/net/dap_chain_node_cli_cmd.c
+++ b/modules/net/dap_chain_node_cli_cmd.c
@@ -1848,30 +1848,30 @@ int com_tx_wallet(int argc, char ** argv, char **str_reply)
             dap_string_append_printf(l_string_ret, "addr: %s\n", (l_addr_str) ? l_addr_str : "-");
             dap_string_append_printf(l_string_ret, "network: %s\n", (l_net_name ) ? l_net_name : "-");
 
-            size_t l_addr_tokens_size = 0;
-            char **l_addr_tokens = NULL;
-            dap_chain_ledger_addr_get_token_ticker_all_fast(l_ledger, l_addr, &l_addr_tokens, &l_addr_tokens_size);
-            if(l_addr_tokens_size > 0)
+            size_t l_l_addr_tokens_size = 0;
+            char **l_l_addr_tokens = NULL;
+            dap_chain_ledger_addr_get_token_ticker_all_fast(l_ledger, l_addr, &l_l_addr_tokens, &l_l_addr_tokens_size);
+            if(l_l_addr_tokens_size > 0)
                 dap_string_append_printf(l_string_ret, "balance:\n");
             else
                 dap_string_append_printf(l_string_ret, "balance: 0");
 
-            for(size_t i = 0; i < l_addr_tokens_size; i++) {
-                if(l_addr_tokens[i]) {
-                    uint256_t l_balance = dap_chain_ledger_calc_balance(l_ledger, l_addr, l_addr_tokens[i]);
+            for(size_t i = 0; i < l_l_addr_tokens_size; i++) {
+                if(l_l_addr_tokens[i]) {
+                    uint256_t l_balance = dap_chain_ledger_calc_balance(l_ledger, l_addr, l_l_addr_tokens[i]);
                     char *l_balance_coins = dap_chain_balance_to_coins(l_balance);
                     char *l_balance_datoshi = dap_chain_balance_print(l_balance);
                     dap_string_append_printf(l_string_ret, "\t%s (%s) %s\n", l_balance_coins,
-                            l_balance_datoshi, l_addr_tokens[i]);
-                    if(i < l_addr_tokens_size - 1)
+                            l_balance_datoshi, l_l_addr_tokens[i]);
+                    if(i < l_l_addr_tokens_size - 1)
                         dap_string_append_printf(l_string_ret, "\n");
                     DAP_DELETE(l_balance_coins);
                     DAP_DELETE(l_balance_datoshi);
 
                 }
-                DAP_DELETE(l_addr_tokens[i]);
+                DAP_DELETE(l_l_addr_tokens[i]);
             }
-            DAP_DELETE(l_addr_tokens);
+            DAP_DELETE(l_l_addr_tokens);
             DAP_DELETE(l_addr_str);
             if(l_wallet)
                 dap_chain_wallet_close(l_wallet);
@@ -3386,10 +3386,9 @@ int com_token_emit(int a_argc, char ** a_argv, char ** a_str_reply)
     const char * l_addr_str = NULL;
 
     const char * l_emission_hash_str = NULL;
-    dap_chain_hash_fast_t l_emission_hash={0};
-    dap_chain_hash_fast_t l_token_emission_hash={0};
-    dap_chain_datum_token_emission_t * l_emission = NULL;
-    char * l_emission_hash_str_base58 = NULL;
+    dap_chain_hash_fast_t l_emission_hash, l_datum_emission_hash;
+    dap_chain_datum_token_emission_t *l_emission = NULL;
+    size_t l_emission_size;
 
     const char * l_certs_str = NULL;
 
@@ -3432,54 +3431,49 @@ int com_token_emit(int a_argc, char ** a_argv, char ** a_str_reply)
     // Token ticker
     dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-token", &l_ticker);
 
-    // Emission value
-    if(dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-emission_value", &str_tmp)) {
-        l_emission_value = dap_chain_balance_scan(str_tmp);
+    if(!l_certs_str) {
+        dap_chain_node_cli_set_reply_text(a_str_reply, "token_emit requires parameter '-certs'");
+        return -4;
     }
+    dap_cert_parse_str_list(l_certs_str, &l_certs, &l_certs_size);
 
-    if (IS_ZERO_256(l_emission_value)) {
-        dap_chain_node_cli_set_reply_text(a_str_reply, "token_emit requires parameter '-emission_value'");
-        return -1;
+    if(!l_certs_size) {
+        dap_chain_node_cli_set_reply_text(a_str_reply,
+                "token_emit command requres at least one valid certificate to sign the basic transaction of emission");
+        return -5;
     }
 
+    const char *l_add_sign = NULL;
+    dap_chain_addr_t *l_addr = NULL;
+    dap_chain_node_cli_find_option_val(a_argv, arg_index, arg_index + 1, "sign", &l_add_sign);
+    if (!l_add_sign) {      //Create the emission
+        // Emission value
+        if(dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-emission_value", &str_tmp)) {
+            l_emission_value = dap_chain_balance_scan(str_tmp);
+        }
 
-    if(!l_addr_str) {
-        dap_chain_node_cli_set_reply_text(a_str_reply, "token_emit requires parameter '-addr'");
-        return -2;
-    }
+        if (IS_ZERO_256(l_emission_value)) {
+            dap_chain_node_cli_set_reply_text(a_str_reply, "token_emit requires parameter '-emission_value'");
+            return -1;
+        }
 
-    if(!l_ticker) {
-        dap_chain_node_cli_set_reply_text(a_str_reply, "token_emit requires parameter '-token'");
-        return -3;
-    }
+        if(!l_addr_str) {
+            dap_chain_node_cli_set_reply_text(a_str_reply, "token_emit requires parameter '-addr'");
+            return -2;
+        }
 
-    if(!l_certs_str && !l_emission_hash_str) {
-        dap_chain_node_cli_set_reply_text(a_str_reply, "token_emit requires parameter '-certs' or '-emission' ");
-        return -4;
-    }
+        if(!l_ticker) {
+            dap_chain_node_cli_set_reply_text(a_str_reply, "token_emit requires parameter '-token'");
+            return -3;
+        }
 
+        dap_chain_addr_t *l_addr = dap_chain_addr_from_str(l_addr_str);
 
-    if (l_emission_hash_str){// Load emission
-        l_emission_hash_str_base58 = dap_enc_base58_encode_hash_to_str(&l_emission_hash);
-        if (dap_chain_hash_fast_from_str( l_emission_hash_str,&l_emission_hash) == 0 ){
-            l_emission = dap_chain_ledger_token_emission_find(l_net->pub.ledger,l_ticker,&l_emission_hash);
-            if (! l_emission){
-                dap_chain_node_cli_set_reply_text(a_str_reply, "Can' find emission with hash \"%s\" for token %s on network %s",
-                                                  l_emission_hash_str, l_ticker, l_net->pub.name);
-                return -32;
-            }
-        }else{
-            dap_chain_node_cli_set_reply_text(a_str_reply, "Hash \"%s\" for parameter '-emission' is invalid", l_emission_hash_str);
-            return -31;
+        if(!l_addr) {
+            dap_chain_node_cli_set_reply_text(a_str_reply, "address \"%s\" is invalid", l_addr_str);
+            return -4;
         }
-    }else if (l_certs_str){ // Load certs
-        dap_cert_parse_str_list(l_certs_str, &l_certs, &l_certs_size);
 
-        if(!l_certs_size) {
-            dap_chain_node_cli_set_reply_text(a_str_reply,
-                    "token_emit command requres at least one valid certificate to sign the basic transaction of emission");
-            return -5;
-        }
         dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-chain_emission", &l_chain_emission_str);
         if(l_chain_emission_str) {
             if((l_chain_emission = dap_chain_net_get_chain_by_name(l_net, l_chain_emission_str)) == NULL) { // Can't find such chain
@@ -3489,15 +3483,22 @@ int com_token_emit(int a_argc, char ** a_argv, char ** a_str_reply)
                 return -45;
             }
         }
-    }
-
-
-
-    dap_chain_addr_t * l_addr = dap_chain_addr_from_str(l_addr_str);
-
-    if(!l_addr) {
-        dap_chain_node_cli_set_reply_text(a_str_reply, "address \"%s\" is invalid", l_addr_str);
-        return -4;
+    } else {
+        if (l_emission_hash_str) {
+            DL_FOREACH(l_net->pub.chains, l_chain_emission) {
+                l_emission = dap_chain_mempool_emission_get(l_chain_emission, l_emission_hash_str);
+                if (l_emission)
+                    break;
+            }
+            if (!l_emission){
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Can' find emission with hash \"%s\" for token %s on network %s",
+                                                  l_emission_hash_str, l_ticker, l_net->pub.name);
+                return -32;
+            }
+        } else {
+            dap_chain_node_cli_set_reply_text(a_str_reply, "Subcommand 'sign' recuires parameter '-emission'");
+            return -31;
+        }
     }
 
     dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-chain_base_tx", &l_chain_base_tx_str);
@@ -3509,146 +3510,67 @@ int com_token_emit(int a_argc, char ** a_argv, char ** a_str_reply)
             DAP_DELETE(l_addr);
             return -47;
         }
-    }
-
-    // Get groups for the chains
-    char *l_gdb_group_mempool_base_tx;
-
-    if(l_chain_base_tx) {
-        l_gdb_group_mempool_base_tx = dap_chain_net_get_gdb_group_mempool(l_chain_base_tx);
-    }
-    else {
-        l_gdb_group_mempool_base_tx = dap_chain_net_get_gdb_group_mempool_by_chain_type(l_net, CHAIN_TYPE_TX);
-    }
-    // Create emission datum
-    // then create datum in memory
-    if (!l_emission) {
-        if (!l_chain_emission) {
-            l_chain_emission = dap_chain_net_get_chain_by_chain_type(l_net, CHAIN_TYPE_EMISSION);
-        }
-        char *l_gdb_group_mempool_emission = dap_chain_net_get_gdb_group_mempool(l_chain_emission);
-        size_t l_emission_size = sizeof(l_emission->hdr) +
-                sizeof(l_emission->data.type_auth.signs_count);
-
-        l_emission = DAP_NEW_Z_SIZE(dap_chain_datum_token_emission_t, l_emission_size);
-        l_emission->hdr.version = 2;
-        l_emission->hdr.value_256 = l_emission_value;
-        strncpy(l_emission->hdr.ticker, l_ticker, sizeof(l_emission->hdr.ticker) - 1);
-        l_emission->hdr.type = DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_AUTH;
-        memcpy(&l_emission->hdr.address, l_addr, sizeof(l_emission->hdr.address));
-        time_t l_time = time(NULL);
-        memcpy(&l_emission->hdr.nonce, &l_time, sizeof(time_t));
-        l_emission->data.type_auth.signs_count = l_certs_size;
-        // Then add signs
-        size_t l_offset = 0;
-        for(size_t i = 0; i < l_certs_size; i++) {
-            dap_sign_t *l_sign = dap_cert_sign(l_certs[i], &l_emission->hdr,
-                    sizeof(l_emission->hdr), 0);
-            size_t l_sign_size = dap_sign_get_size(l_sign);
-            l_emission_size += l_sign_size;
-            l_emission = DAP_REALLOC(l_emission, l_emission_size);
-            memcpy(l_emission->data.type_auth.signs + l_offset, l_sign, l_sign_size);
-            l_offset += l_sign_size;
-            DAP_DELETE(l_sign);
-        }
-
-        // Produce datum
-        dap_chain_datum_t *l_datum_emission = dap_chain_datum_create(DAP_CHAIN_DATUM_TOKEN_EMISSION,
-                l_emission,
-                l_emission_size);
-        size_t l_datum_emission_size = sizeof(l_datum_emission->header) + l_datum_emission->header.data_size;
-
-        // Calc datum emission's hash
-        dap_hash_fast(l_datum_emission, l_datum_emission_size, &l_emission_hash);
-        // Calc token's hash
-        dap_hash_fast(l_emission, l_emission_size, &l_token_emission_hash);
-        //dap_hash_fast(l_emission, l_datum_emission_size, &l_emission_hash);
-        l_emission_hash_str = dap_chain_hash_fast_to_str_new(&l_emission_hash);
-        l_emission_hash_str_base58 = dap_enc_base58_encode_hash_to_str(&l_emission_hash);
-
-        // Delete token emission
-        DAP_DEL_Z(l_emission);
-
-        // Add token emission datum to mempool
-        bool l_placed = dap_chain_global_db_gr_set(dap_strdup(l_emission_hash_str),
-                                                   (uint8_t *)l_datum_emission,
-                                                   l_datum_emission_size,
-                                                   l_gdb_group_mempool_emission);
-        str_reply_tmp = dap_strdup_printf("Datum %s with 256bit emission is%s placed in datum pool",
-                                          dap_strcmp(l_hash_out_type, "hex") ? l_emission_hash_str_base58 : l_emission_hash_str,
-                                          l_placed ? "" : " not");
-        DAP_DELETE((char *)l_emission_hash_str);
-        DAP_DEL_Z(l_emission_hash_str_base58);
-        if (!l_placed) {
-            DAP_DEL_Z(l_datum_emission);
-            return -1;
-        }
-        l_datum_emission = NULL;
-    } // TODO possible update emission if it found, or remove -emission parameter
-
-    // create first transaction (with tx_token)
-    dap_chain_datum_tx_t *l_tx = DAP_NEW_Z_SIZE(dap_chain_datum_tx_t, sizeof(dap_chain_datum_tx_t));
-    l_tx->header.ts_created = time(NULL);
-    dap_chain_hash_fast_t l_tx_prev_hash = { 0 };
-    // create items
-
-    dap_chain_tx_token_t *l_tx_token = dap_chain_datum_tx_item_token_create(l_chain_emission->id, &l_token_emission_hash, l_ticker);
-    dap_chain_tx_in_t *l_in = dap_chain_datum_tx_item_in_create(&l_tx_prev_hash, 0);
-    dap_chain_tx_out_t *l_out = dap_chain_datum_tx_item_out_create(l_addr, l_emission_value);
-
-    // pack items to transaction
-    dap_chain_datum_tx_add_item(&l_tx, (const uint8_t*) l_tx_token);
-    dap_chain_datum_tx_add_item(&l_tx, (const uint8_t*) l_in);
-    dap_chain_datum_tx_add_item(&l_tx, (const uint8_t*) l_out);
-
-    if (l_certs){
-        // Sign all that we have with certs
-        for(size_t i = 0; i < l_certs_size; i++) {
-            if(dap_chain_datum_tx_add_sign_item(&l_tx, l_certs[i]->enc_key) < 0) {
-                dap_chain_node_cli_set_reply_text(a_str_reply, "No private key for certificate=%s",
-                        l_certs[i]->name);
-                DAP_DELETE(l_addr);
-                return -3;
-            }
+        if(!l_ticker) {
+            dap_chain_node_cli_set_reply_text(a_str_reply, "token_emit requires parameter '-token'");
+            return -3;
         }
     }
 
-    if (l_certs)
-        DAP_DEL_Z(l_certs);
-
-    DAP_DEL_Z(l_tx_token);
-    DAP_DEL_Z(l_in);
-    DAP_DEL_Z(l_out);
-
-    size_t l_tx_size = dap_chain_datum_tx_get_size(l_tx);
-
-    // Pack transaction into the datum
-    dap_chain_datum_t * l_datum_tx = dap_chain_datum_create(DAP_CHAIN_DATUM_TX, l_tx, l_tx_size);
-    size_t l_datum_tx_size = dap_chain_datum_size(l_datum_tx);
-
-    // calc datum hash
-    dap_chain_hash_fast_t l_datum_tx_hash;
-    dap_hash_fast(l_datum_tx, l_datum_tx_size,  &l_datum_tx_hash);
-    char * l_tx_hash_str = dap_chain_hash_fast_to_str_new(&l_datum_tx_hash);
-    char * l_tx_hash_str_base58 = dap_enc_base58_encode_hash_to_str(&l_datum_tx_hash);
-    DAP_DEL_Z(l_tx);
-
-    // Add to mempool tx token
-    bool l_placed = dap_chain_global_db_gr_set(dap_strdup(l_tx_hash_str), l_datum_tx,
-                                               l_datum_tx_size, l_gdb_group_mempool_base_tx);
-    dap_chain_node_cli_set_reply_text(a_str_reply, "%s\nDatum %s with 256bit TX is%s placed in datum pool ",
-                                      str_reply_tmp,
-                                      dap_strcmp(l_hash_out_type, "hex") ? l_tx_hash_str_base58 : l_tx_hash_str,
-                                      l_placed ? "" : " not");
-    DAP_DEL_Z(l_tx_hash_str);
-    DAP_DEL_Z(l_tx_hash_str_base58);
-    DAP_DELETE(str_reply_tmp);
-    DAP_DELETE(l_addr);
+    //dap_chain_mempool_emission_create(l_emission, DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_AUTH, l_ticker, l_emission_value);
+    if (!l_add_sign) {
+        if (!l_chain_emission)
+            l_chain_emission = dap_chain_net_get_chain_by_chain_type(l_net, CHAIN_TYPE_EMISSION);
+        // Create emission datum
+        l_emission = dap_chain_datum_emission_create(l_emission_value, l_ticker, l_addr);
+    }
+    l_emission->data.type_auth.signs_count += l_certs_size;
+    // Then add signs
+    for(size_t i = 0; i < l_certs_size; i++)
+        l_emission = dap_chain_datum_emission_add_sign(l_certs[i]->enc_key, l_emission);
+    // Calc emission's hash
+    l_emission_size = dap_chain_datum_emission_get_size((uint8_t *)l_emission);
+    dap_hash_fast(l_emission, l_emission_size, &l_emission_hash);
+    // Produce datum
+    dap_chain_datum_t *l_datum_emission = dap_chain_datum_create(DAP_CHAIN_DATUM_TOKEN_EMISSION,
+            l_emission,
+            l_emission_size);
+    // Delete token emission
+    DAP_DEL_Z(l_emission);
+    size_t l_datum_emission_size = sizeof(l_datum_emission->header) + l_datum_emission->header.data_size;
+
+    // Calc datum emission's hash
+    dap_hash_fast(l_datum_emission, l_datum_emission_size, &l_datum_emission_hash);
+    bool l_hex_format = dap_strcmp(l_hash_out_type, "hex");
+    l_emission_hash_str = l_hex_format ? dap_chain_hash_fast_to_str_new(&l_datum_emission_hash)
+                                       : dap_enc_base58_encode_hash_to_str(&l_datum_emission_hash);
+    // Add token emission datum to mempool
+    char *l_gdb_group_mempool_emission = dap_chain_net_get_gdb_group_mempool(l_chain_emission);
+    bool l_placed = dap_chain_global_db_gr_set(l_emission_hash_str,
+                                               (uint8_t *)l_datum_emission,
+                                               l_datum_emission_size,
+                                               l_gdb_group_mempool_emission);
+    str_reply_tmp = dap_strdup_printf("Datum %s with 256bit emission is%s placed in datum pool",
+                                      l_emission_hash_str, l_placed ? "" : " not");
+    DAP_DEL_Z(l_emission_hash_str);
     if (!l_placed) {
-        DAP_DELETE(l_datum_tx);
-        return -2;
+        DAP_DEL_Z(l_datum_emission);
+        DAP_DEL_Z(l_certs);
+        return -1;
     }
 
+    if(l_chain_base_tx) {
+        dap_chain_hash_fast_t *l_datum_tx_hash = dap_chain_mempool_base_tx_create(l_chain_base_tx, &l_emission_hash,
+                                                                l_chain_emission->id, l_emission_value, l_ticker,
+                                                                l_addr, l_certs, l_certs_size);
+        char *l_tx_hash_str = l_hex_format ? dap_chain_hash_fast_to_str_new(l_datum_tx_hash)
+                                           : dap_enc_base58_encode_hash_to_str(l_datum_tx_hash);
+        dap_chain_node_cli_set_reply_text(a_str_reply, "%s\nDatum %s with 256bit TX is%s placed in datum pool",
+                                          str_reply_tmp, l_tx_hash_str, l_placed ? "" : " not");
+        DAP_DEL_Z(l_tx_hash_str);
+        DAP_DELETE(str_reply_tmp);
+    }
+    DAP_DELETE(l_addr);
+    DAP_DEL_Z(l_certs);
     return 0;
 }
 
@@ -4033,17 +3955,26 @@ int com_tx_create(int argc, char ** argv, char **str_reply)
     const char * l_token_ticker = NULL;
     const char * l_net_name = NULL;
     const char * l_chain_name = NULL;
+    const char * l_emission_chain_name = NULL;
     const char * l_tx_num_str = NULL;
+    const char *l_emission_hash_str = NULL;
+    const char *l_certs_str = NULL;
+    dap_cert_t **l_certs = NULL;
+    size_t l_certs_count = 0;
+    dap_chain_hash_fast_t l_emission_hash = {};
     size_t l_tx_num = 0;
 
     uint256_t l_value = {};
     uint256_t l_value_fee = {};
     dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-from_wallet", &l_from_wallet_name);
+    dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-from_emission", &l_emission_hash_str);
+    dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-emission_chain", &l_emission_chain_name);
     dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-to_addr", &addr_base58_to);
     dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-token", &l_token_ticker);
     dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-net", &l_net_name);
     dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-chain", &l_chain_name);
     dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-tx_num", &l_tx_num_str);
+    dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-certs", &l_certs_str);
 
     if(l_tx_num_str)
         l_tx_num = strtoul(l_tx_num_str, NULL, 10);
@@ -4056,39 +3987,60 @@ int com_tx_create(int argc, char ** argv, char **str_reply)
     if(dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-value", &str_tmp)) {
         l_value = dap_chain_balance_scan(str_tmp);
     }
-    if(!l_from_wallet_name) {
-        dap_chain_node_cli_set_reply_text(str_reply, "tx_create requires parameter '-from_wallet'");
+    if(!l_from_wallet_name && !l_emission_hash_str) {
+        dap_chain_node_cli_set_reply_text(str_reply, "tx_create requires one of parameters '-from_wallet' or '-from_emission'");
         return -1;
     }
     if(!addr_base58_to) {
         dap_chain_node_cli_set_reply_text(str_reply, "tx_create requires parameter '-to_addr'");
-        return -1;
-    }
-    if(IS_ZERO_256(l_value)) {
-        dap_chain_node_cli_set_reply_text(str_reply, "tx_create requires parameter '-value'");
-        return -1;
-    }
-    if(addr_base58_fee && IS_ZERO_256(l_value_fee)) {
-        dap_chain_node_cli_set_reply_text(str_reply,
-                "tx_create requires parameter '-value_fee' if '-fee' is specified");
-        return -1;
+        return -2;
     }
 
     if(!l_net_name) {
         dap_chain_node_cli_set_reply_text(str_reply, "tx_create requires parameter '-net'");
-        return -1;
+        return -6;
     }
     dap_chain_net_t * l_net = dap_chain_net_by_name(l_net_name);
     dap_ledger_t *l_ledger = l_net ? l_net->pub.ledger : NULL;
     if(l_net == NULL || (l_ledger = dap_chain_ledger_by_net_name(l_net_name)) == NULL) {
         dap_chain_node_cli_set_reply_text(str_reply, "not found net by name '%s'", l_net_name);
-        return -1;
+        return -7;
+    }
+
+    dap_chain_t *l_emission_chain;
+    if (l_emission_hash_str) {
+        if (dap_chain_hash_fast_from_str(l_emission_hash_str, &l_emission_hash)) {
+            dap_chain_node_cli_set_reply_text(str_reply, "tx_create requires parameter '-emission_hash' "
+                                                         "to be valid string containing hash in hex or base58 format");
+            return -3;
+        }
+        if (!l_emission_chain_name ||
+                (l_emission_chain = dap_chain_net_get_chain_by_name(l_net, l_emission_chain_name)) == NULL) {
+            dap_chain_node_cli_set_reply_text(str_reply, "tx_create requires parameter '-emission_chain' "
+                                                         "to be a valid chain name");
+            return -9;
+        }
+        if(!l_certs_str) {
+            dap_chain_node_cli_set_reply_text(str_reply, "tx_create requires parameter '-certs'");
+            return -4;
+        }
+        dap_cert_parse_str_list(l_certs_str, &l_certs, &l_certs_count);
+        if(!l_certs_count) {
+            dap_chain_node_cli_set_reply_text(str_reply,
+                    "tx_create requires at least one valid certificate to sign the basic transaction of emission");
+            return -5;
+        }
+    }
+    if(IS_ZERO_256(l_value)) {
+        dap_chain_node_cli_set_reply_text(str_reply, "tx_create requires parameter '-value' to be valid uint256 value");
+        return -4;
+    }
+    if(addr_base58_fee && IS_ZERO_256(l_value_fee)) {
+        dap_chain_node_cli_set_reply_text(str_reply,
+                "tx_create requires parameter '-value_fee' to be valid uint256 value if '-fee' is specified");
+        return -5;
     }
 
-    /*    if(!l_chain_name) {
-     dap_chain_node_cli_set_reply_text(str_reply, "tx_create requires parameter '-chain'");
-     return -1;
-     }*/
     dap_chain_t * l_chain = dap_chain_net_get_chain_by_name(l_net, l_chain_name);
     if(!l_chain) {
         l_chain = dap_chain_net_get_chain_by_chain_type(l_net, CHAIN_TYPE_TX);
@@ -4096,7 +4048,33 @@ int com_tx_create(int argc, char ** argv, char **str_reply)
     if(!l_chain) {
         dap_chain_node_cli_set_reply_text(str_reply, "not found chain name '%s', try use parameter '-chain'",
                 l_chain_name);
-        return -1;
+        return -8;
+    }
+
+    dap_chain_addr_t *l_addr_to = dap_chain_addr_from_str(addr_base58_to);
+    if(!l_addr_to) {
+        dap_chain_node_cli_set_reply_text(str_reply, "destination address is invalid");
+        return -11;
+    }
+
+    dap_string_t *string_ret = dap_string_new(NULL);
+    int res = 0;
+    if (l_emission_hash_str) {
+        dap_hash_fast_t *l_tx_hash = dap_chain_mempool_base_tx_create(l_chain, &l_emission_hash, l_emission_chain->id,
+                                                                      l_value, l_token_ticker, l_addr_to, l_certs, l_certs_count);
+        if (l_tx_hash){
+            char l_tx_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE];
+            dap_chain_hash_fast_to_str(l_tx_hash,l_tx_hash_str,sizeof (l_tx_hash_str)-1);
+            dap_string_append_printf(string_ret, "transfer=Ok\ntx_hash=%s\n",l_tx_hash_str);
+            DAP_DELETE(l_tx_hash);
+        }else{
+            dap_string_append_printf(string_ret, "transfer=False\n");
+            res = -15;
+        }
+        dap_chain_node_cli_set_reply_text(str_reply, string_ret->str);
+        dap_string_free(string_ret, false);
+        DAP_DELETE(l_addr_to);
+        return res;
     }
 
     const char *c_wallets_path = dap_chain_wallet_get_path(g_config);
@@ -4104,49 +4082,36 @@ int com_tx_create(int argc, char ** argv, char **str_reply)
 
     if(!l_wallet) {
         dap_chain_node_cli_set_reply_text(str_reply, "wallet %s does not exist", l_from_wallet_name);
-        return -1;
+        return -9;
     }
     const dap_chain_addr_t *addr_from = (const dap_chain_addr_t *) dap_chain_wallet_get_addr(l_wallet, l_net->pub.id);
-    dap_chain_addr_t *addr_to = dap_chain_addr_from_str(addr_base58_to);
+
     dap_chain_addr_t *addr_fee = dap_chain_addr_from_str(addr_base58_fee);
 
     if(!addr_from) {
         dap_chain_node_cli_set_reply_text(str_reply, "source address is invalid");
-        return -1;
-    }
-    if(!addr_to) {
-        dap_chain_node_cli_set_reply_text(str_reply, "destination address is invalid");
-        return -1;
+        return -10;
     }
     if(addr_base58_fee && !addr_fee) {
         dap_chain_node_cli_set_reply_text(str_reply, "fee address is invalid");
-        return -1;
+        return -12;
     }
-
-    //
     // Check, if network ID is same as ID in destination wallet address. If not - operation is cancelled.
-    //
-
-    if (addr_to->net_id.uint64 != l_net->pub.id.uint64)
-    {
+    if (l_addr_to->net_id.uint64 != l_net->pub.id.uint64) {
         dap_chain_node_cli_set_reply_text(str_reply, "destination wallet network ID=0x%llx and network ID=0x%llx is not equal. Please, change network name or wallet address", 
-                                            addr_to->net_id.uint64, l_net->pub.id.uint64);
-        return -1;
+                                            l_addr_to->net_id.uint64, l_net->pub.id.uint64);
+        return -13;
     }
 
-    dap_string_t *string_ret = dap_string_new(NULL);
-    //g_string_printf(string_ret, "from=%s\nto=%s\nval=%lld\nfee=%s\nval_fee=%lld\n\n",
-    //        addr_base58_from, addr_base58_to, value, addr_base58_fee, value_fee);
-    int res = 0;
     if(l_tx_num){
         res = dap_chain_mempool_tx_create_massive(l_chain, dap_chain_wallet_get_key(l_wallet, 0), addr_from,
-                               addr_to, addr_fee,
+                               l_addr_to, addr_fee,
                                l_token_ticker, l_value, l_value_fee, l_tx_num);
 
         dap_string_append_printf(string_ret, "transfer=%s\n",
                 (res == 0) ? "Ok" : (res == -2) ? "False, not enough funds for transfer" : "False");
     }else{
-        dap_hash_fast_t * l_tx_hash = dap_chain_mempool_tx_create(l_chain, dap_chain_wallet_get_key(l_wallet, 0), addr_from, addr_to,
+        dap_hash_fast_t * l_tx_hash = dap_chain_mempool_tx_create(l_chain, dap_chain_wallet_get_key(l_wallet, 0), addr_from, l_addr_to,
                 addr_fee,
                 l_token_ticker, l_value, l_value_fee);
         if (l_tx_hash){
@@ -4156,7 +4121,7 @@ int com_tx_create(int argc, char ** argv, char **str_reply)
             DAP_DELETE(l_tx_hash);
         }else{
             dap_string_append_printf(string_ret, "transfer=False\n");
-            res = -1;
+            res = -14;
         }
 
     }
@@ -4164,7 +4129,7 @@ int com_tx_create(int argc, char ** argv, char **str_reply)
     dap_chain_node_cli_set_reply_text(str_reply, string_ret->str);
     dap_string_free(string_ret, false);
 
-    DAP_DELETE(addr_to);
+    DAP_DELETE(l_addr_to);
     DAP_DELETE(addr_fee);
     dap_chain_wallet_close(l_wallet);
     return res;
@@ -4202,9 +4167,9 @@ int com_tx_verify(int a_argc, char **a_argv, char **a_str_reply)
     }
     dap_hash_fast_t l_tx_hash;
     char *l_hex_str_from58 = NULL;
-    if (dap_chain_hash_fast_from_str(l_tx_hash_str, &l_tx_hash) < 0) {
+    if (dap_chain_hash_fast_from_hex_str(l_tx_hash_str, &l_tx_hash)) {
         l_hex_str_from58 = dap_enc_base58_to_hex_str_from_str(l_tx_hash_str);
-        if (!l_hex_str_from58) {
+        if (dap_chain_hash_fast_from_hex_str(l_hex_str_from58, &l_tx_hash)) {
             dap_chain_node_cli_set_reply_text(a_str_reply, "Invalid tx hash format, need hex or base58");
             return -3;
         }
@@ -4213,12 +4178,14 @@ int com_tx_verify(int a_argc, char **a_argv, char **a_str_reply)
     char *l_gdb_group = dap_chain_net_get_gdb_group_mempool(l_chain);
     dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t *)
             dap_chain_global_db_gr_get(l_hex_str_from58 ? l_hex_str_from58 : l_tx_hash_str, &l_tx_size, l_gdb_group);
+    DAP_DEL_Z(l_hex_str_from58);
     if (!l_tx) {
         dap_chain_node_cli_set_reply_text(a_str_reply, "Specified tx not found");
         return -3;
     }
-    if (dap_chain_ledger_tx_add_check(l_net->pub.ledger, l_tx)) {
-        dap_chain_node_cli_set_reply_text(a_str_reply, "Specified tx verify fail!");
+    int l_ret = dap_chain_ledger_tx_add_check(l_net->pub.ledger, l_tx);
+    if (l_ret) {
+        dap_chain_node_cli_set_reply_text(a_str_reply, "Specified tx verify fail with return code=%d", l_ret);
         return -4;
     }
     dap_chain_node_cli_set_reply_text(a_str_reply, "Specified tx verified successfully");
diff --git a/modules/net/dap_chain_node_cli_cmd_tx.c b/modules/net/dap_chain_node_cli_cmd_tx.c
index 141b3cdf86..2232fa1172 100644
--- a/modules/net/dap_chain_node_cli_cmd_tx.c
+++ b/modules/net/dap_chain_node_cli_cmd_tx.c
@@ -1323,8 +1323,7 @@ int com_ledger(int a_argc, char ** a_argv, char **a_str_reply)
         //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) &&
-                    dap_enc_base58_hex_to_hash(l_tx_hash_str, &l_tx_hash)) {
+            if (dap_chain_hash_fast_from_str(l_tx_hash_str, &l_tx_hash)) {
                 l_tx_hash_str = NULL;
                 dap_chain_node_cli_set_reply_text(a_str_reply, "tx hash not recognized");
                 return -1;
@@ -1465,8 +1464,7 @@ int com_ledger(int a_argc, char ** a_argv, char **a_str_reply)
             return -2;
         }
         dap_chain_hash_fast_t *l_tx_hash = DAP_NEW(dap_chain_hash_fast_t);
-        if (dap_chain_hash_fast_from_str(l_tx_hash_str, l_tx_hash) &&
-                dap_enc_base58_hex_to_hash(l_tx_hash_str, l_tx_hash)) {
+        if (dap_chain_hash_fast_from_str(l_tx_hash_str, l_tx_hash)) {
             dap_chain_node_cli_set_reply_text(a_str_reply, "Can't get hash_fast from %s", l_tx_hash_str);
             return -4;
         }
diff --git a/modules/type/blocks/dap_chain_cs_blocks.c b/modules/type/blocks/dap_chain_cs_blocks.c
index 106ee3bee2..1d03a0911f 100644
--- a/modules/type/blocks/dap_chain_cs_blocks.c
+++ b/modules/type/blocks/dap_chain_cs_blocks.c
@@ -277,7 +277,7 @@ static int s_cli_parse_cmd_hash(char ** a_argv, int a_arg_index, int a_argc, cha
     const char *l_datum_hash_str = NULL;
     dap_chain_node_cli_find_option_val(a_argv, a_arg_index, a_argc, a_param, &l_datum_hash_str);
 
-    return dap_enc_base58_hex_to_hash(l_datum_hash_str, a_datum_hash);
+    return dap_chain_hash_fast_from_str(l_datum_hash_str, a_datum_hash);
 }
 
 /**
@@ -455,7 +455,7 @@ static int s_cli_blocks(int a_argc, char ** a_argv, char **a_str_reply)
             dap_chain_block_t  * l_block;
             size_t l_block_size = 0;
             dap_chain_hash_fast_t l_block_hash={0};
-            dap_enc_base58_hex_to_hash( l_subcmd_str_arg, &l_block_hash); // Convert argument to hash
+            dap_chain_hash_fast_from_str( l_subcmd_str_arg, &l_block_hash); // Convert argument to hash
             l_block = (dap_chain_block_t*) dap_chain_get_atom_by_hash( l_chain, &l_block_hash, &l_block_size);
             if ( l_block){
                 dap_chain_block_cache_t *l_block_cache = dap_chain_block_cs_cache_get_by_hash(l_blocks, &l_block_hash);
-- 
GitLab