From bd5d68bea1a5ab4b1cb18dd08979f315dd297ca7 Mon Sep 17 00:00:00 2001
From: Roman Khlopkov <roman.khlopkov@demlabs.net>
Date: Wed, 24 Nov 2021 08:00:31 +0000
Subject: [PATCH] bugs-5094

---
 CMakeLists.txt                                |  24 +-
 dap-sdk/core/include/dap_module.h             |   2 +-
 dap-sdk/crypto/src/dap_hash.c                 |   2 +-
 modules/chain/dap_chain_ledger.c              | 139 +++--
 modules/chain/include/dap_chain_ledger.h      |  13 +-
 modules/common/dap_chain_datum_token.c        |  54 ++
 modules/common/dap_chain_datum_tx_items.c     |   4 +-
 .../common/include/dap_chain_datum_token.h    |  17 +-
 .../common/include/dap_chain_datum_tx_items.h |   3 +-
 .../include/dap_chain_datum_tx_out_cond.h     |   5 +-
 .../block-poa/dap_chain_cs_block_poa.c        |  11 +
 .../include/dap_chain_cs_block_poa.h          |   1 +
 .../block-pos/dap_chain_cs_block_pos.c        |  46 +-
 .../consensus/dag-poa/dap_chain_cs_dag_poa.c  |  29 +-
 .../dag-poa/include/dap_chain_cs_dag_poa.h    |   2 +
 .../consensus/dag-pos/dap_chain_cs_dag_pos.c  |  64 +--
 modules/consensus/none/dap_chain_cs_none.c    |   3 +-
 modules/net/dap_chain_net.c                   |  10 +-
 modules/net/dap_chain_node_cli_cmd.c          |  17 +-
 modules/net/dap_chain_node_cli_cmd_tx.c       | 255 +--------
 modules/net/srv/CMakeLists.txt                |  11 +-
 modules/net/srv/dap_chain_net_srv_order.c     |  10 +-
 modules/service/stake/CMakeLists.txt          |   2 +-
 .../service/stake/dap_chain_net_srv_stake.c   | 533 ++++++++++++++----
 .../stake/include/dap_chain_net_srv_stake.h   |  16 +-
 .../xchange/dap_chain_net_srv_xchange.c       |   4 +-
 .../include/dap_chain_net_srv_xchange.h       |   2 +-
 modules/type/blocks/dap_chain_cs_blocks.c     |   3 +-
 modules/type/dag/dap_chain_cs_dag.c           |   3 +-
 29 files changed, 724 insertions(+), 561 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 10e86caa95..270e002866 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -7,6 +7,14 @@ add_definitions ("-DCELLFRAME_SDK_VERSION=\"${CELLFRAME_SDK_NATIVE_VERSION}\"")
 set(DAPSDK_MODULES "")
 message("Cellframe modules: ${CELLFRAME_MODULES}")
 
+if (CELLFRAME_MODULES MATCHES "modules-dynamic")
+    add_definitions("-DDAP_MODULES_DYNAMIC")
+endif()
+
+if (CELLFRAME_MODULES MATCHES "srv-stake")
+     add_definitions("-DDAP_SRV_STAKE_USED")
+endif()
+
 if (CELLFRAME_MODULES MATCHES "core")
     SET(DAPSDK_MODULES "${DAPSDK_MODULES} core crypto")
 endif()
@@ -138,45 +146,43 @@ endif()
 # Enable service Application
 if (CELLFRAME_MODULES MATCHES "srv-app")
     message("[+] Module 'srv-app'")
-    set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_chain_net_srv_app )
+    set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_chain_net_srv_app)
 endif()
 
 # Enable service Application DB
 if (CELLFRAME_MODULES MATCHES "srv-app-db")
     message("[+] Module 'srv-app-db'")
-    set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_chain_net_srv_app_db )
+    set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_chain_net_srv_app_db)
 endif()
 
 # Enable service datum process
 if (CELLFRAME_MODULES MATCHES "srv-datum")
     message("[+] Module 'srv-datum'")
-    set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_chain_net_srv_datum )
+    set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_chain_net_srv_datum)
 endif()
 
 # Enable service VPN
 if (CELLFRAME_MODULES MATCHES "srv-vpn")
     message("[+] Module 'srv-vpn'")
-    set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_chain_net_srv_vpn )
+    set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_chain_net_srv_vpn)
 endif()
 
 # Enable service eXchange
 if (CELLFRAME_MODULES MATCHES "srv-xchange")
     message("[+] Module 'srv-xchange'")
-    set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_chain_net_srv_xchange )
+    set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_chain_net_srv_xchange)
 endif()
 
 # Enable service of delegated stake
 if (CELLFRAME_MODULES MATCHES "srv-stake")
     message("[+] Module 'srv-stake'")
-    set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_chain_net_srv_stake )
-    add_definitions("-DDAP_SRV_STAKE_USED")
+    set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_chain_net_srv_stake)
 endif()
 
 # Enable service for dynamic modules
 if (CELLFRAME_MODULES MATCHES "modules-dynamic")
     message("[+] Module 'dap_modules_dynamic_cdb'")
-    set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_modules_dynamic_cdb )
-    add_definitions("-DDAP_MODULES_DYNAMIC")
+    set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_modules_dynamic_cdb)
 endif()
 
 if (WIN32)
diff --git a/dap-sdk/core/include/dap_module.h b/dap-sdk/core/include/dap_module.h
index b3193c43dc..bae7e6df32 100755
--- a/dap-sdk/core/include/dap_module.h
+++ b/dap-sdk/core/include/dap_module.h
@@ -35,7 +35,7 @@ typedef struct dap_module {
 #define DAP_MODULE_ARGS_MAX  10
 typedef struct dap_module_args {
     const char * name;
-    const char * args[DAP_MODULE_ARGS_MAX]; // ARGS could me not more than DAP_MODULE_ARGS_MAX define
+    const char * args[DAP_MODULE_ARGS_MAX]; // ARGS could be not more than DAP_MODULE_ARGS_MAX define
 } dap_module_args_t;
 
 int dap_module_add(const char * a_name, unsigned int a_version, const char * a_dependensies,
diff --git a/dap-sdk/crypto/src/dap_hash.c b/dap-sdk/crypto/src/dap_hash.c
index a826d81f6f..7a62574684 100755
--- a/dap-sdk/crypto/src/dap_hash.c
+++ b/dap-sdk/crypto/src/dap_hash.c
@@ -54,7 +54,7 @@ int dap_chain_hash_fast_from_str( const char * a_hash_str, dap_chain_hash_fast_t
             *(a_hash->raw + l_offset / 2 - 1) = l_byte;
         }
         return  0;
-    }else  // Wromg string len
+    }else  // Wrong string len
         return -1;
 }
 
diff --git a/modules/chain/dap_chain_ledger.c b/modules/chain/dap_chain_ledger.c
index 3c6546b95c..8a8b7387df 100644
--- a/modules/chain/dap_chain_ledger.c
+++ b/modules/chain/dap_chain_ledger.c
@@ -918,7 +918,7 @@ static void s_treshold_emissions_proc(dap_ledger_t * a_ledger)
         pthread_rwlock_rdlock(&PVT(a_ledger)->treshold_emissions_rwlock);
         HASH_ITER(hh, PVT(a_ledger)->treshold_emissions, l_emission_item, l_emission_tmp) {
             pthread_rwlock_unlock(&PVT(a_ledger)->treshold_emissions_rwlock);
-            int l_res = dap_chain_ledger_token_emission_add(a_ledger, l_emission_item->datum_token_emission,
+            int l_res = dap_chain_ledger_token_emission_add(a_ledger, (byte_t *)l_emission_item->datum_token_emission,
                                                             l_emission_item->datum_token_emission_size);
             if (!l_res) {
                 pthread_rwlock_wrlock(&PVT(a_ledger)->treshold_emissions_rwlock);
@@ -1003,11 +1003,14 @@ void dap_chain_ledger_load_cache(dap_ledger_t *a_ledger)
             continue;
         dap_chain_ledger_token_emission_item_t *l_emission_item = DAP_NEW_Z(dap_chain_ledger_token_emission_item_t);
         dap_chain_hash_fast_from_str(l_objs[i].key, &l_emission_item->datum_token_emission_hash);
-        l_emission_item->datum_token_emission = DAP_NEW_Z_SIZE(dap_chain_datum_token_emission_t, l_objs[i].value_len);
-        memcpy(l_emission_item->datum_token_emission, l_objs[i].value, l_objs[i].value_len);
-        const char * c_token_ticker = l_emission_item->datum_token_emission->hdr.ticker;
+        size_t l_emission_size = l_objs[i].value_len;
+        const char *c_token_ticker = ((dap_chain_datum_token_emission_t *)l_objs[i].value)->hdr.ticker;
         dap_chain_ledger_token_item_t *l_token_item = NULL;
         HASH_FIND_STR(l_ledger_pvt->tokens, c_token_ticker, l_token_item);
+        l_emission_item->datum_token_emission = l_token_item
+                                                           ? dap_chain_datum_emission_read(l_objs[i].value, &l_emission_size)
+                                                           : DAP_DUP_SIZE(l_objs[i].value, l_objs[i].value_len);
+        l_emission_item->datum_token_emission_size = l_emission_size;
         if (l_token_item) {
             HASH_ADD(hh, l_token_item->token_emissions, datum_token_emission_hash,
                      sizeof(dap_chain_hash_fast_t), l_emission_item);
@@ -1111,13 +1114,12 @@ dap_ledger_t* dap_chain_ledger_create(uint16_t a_check_flags, char *a_net_name)
     return l_ledger;
 }
 
-int dap_chain_ledger_token_emission_add_check(dap_ledger_t *a_ledger, const dap_chain_datum_token_emission_t *a_token_emission
-                                        , size_t a_token_emission_size)
+int dap_chain_ledger_token_emission_add_check(dap_ledger_t *a_ledger, byte_t *a_token_emission, size_t a_token_emission_size)
 {
     int ret = 0;
     dap_ledger_private_t *l_ledger_priv = PVT(a_ledger);
 
-    const char * c_token_ticker = a_token_emission->hdr.ticker;
+    const char * c_token_ticker = ((dap_chain_datum_token_emission_t *)a_token_emission)->hdr.ticker;
     dap_chain_ledger_token_item_t * l_token_item = NULL;
     pthread_rwlock_rdlock(&l_ledger_priv->tokens_rwlock);
     HASH_FIND_STR(l_ledger_priv->tokens, c_token_ticker, l_token_item);
@@ -1140,7 +1142,7 @@ int dap_chain_ledger_token_emission_add_check(dap_ledger_t *a_ledger, const dap_
     if(l_token_emission_item ) {
         if(s_debug_more)
             log_it(L_ERROR, "Can't add token emission datum of %"DAP_UINT64_FORMAT_U" %s ( %s ): already present in cache",
-                a_token_emission->hdr.value, c_token_ticker, l_hash_str);
+                l_token_emission_item->datum_token_emission->hdr.value, c_token_ticker, l_hash_str);
         ret = -1;
     }else if ( (! l_token_item) && ( l_threshold_emissions_count >= s_treshold_emissions_max)) {
         if(s_debug_more)
@@ -1148,20 +1150,22 @@ int dap_chain_ledger_token_emission_add_check(dap_ledger_t *a_ledger, const dap_
                s_treshold_emissions_max);
         ret = -2;
     }else{ // Chech emission correctness
-        switch (a_token_emission->hdr.type){
+        size_t l_emission_size = a_token_emission_size;
+        dap_chain_datum_token_emission_t *l_emission = dap_chain_datum_emission_read(a_token_emission, &l_emission_size);
+        switch (l_emission->hdr.type){
             case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_AUTH:{
                 dap_chain_ledger_token_item_t *l_token_item=NULL;
                 pthread_rwlock_rdlock(&PVT(a_ledger)->tokens_rwlock);
-                HASH_FIND_STR(PVT(a_ledger)->tokens, a_token_emission->hdr.ticker, l_token_item);
+                HASH_FIND_STR(PVT(a_ledger)->tokens, l_emission->hdr.ticker, l_token_item);
                 pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock);
                 if (l_token_item){
                     assert(l_token_item->datum_token);
                     if( PVT(a_ledger)->net->pub.token_emission_signs_verify  ){
-                        dap_sign_t * l_sign =(dap_sign_t*) a_token_emission->data.type_auth.signs;
-                        size_t l_offset= (byte_t*)a_token_emission - (byte_t*) l_sign;
+                        dap_sign_t * l_sign =(dap_sign_t*) l_emission->data.type_auth.signs;
+                        size_t l_offset= (byte_t*)l_emission - (byte_t*) l_sign;
                         uint16_t l_aproves = 0, l_aproves_valid = l_token_item->auth_signs_valid;
 
-                        for (uint16_t i=0; i <a_token_emission->data.type_auth.signs_count && l_offset < a_token_emission_size; i++){
+                        for (uint16_t i=0; i <l_emission->data.type_auth.signs_count && l_offset < l_emission_size; i++){
                             size_t l_sign_size = dap_sign_get_size(l_sign);
                             l_sign = (dap_sign_t*) ((byte_t*) l_sign + l_sign_size);
                             if (UINT16_MAX-l_offset> l_sign_size ){
@@ -1171,10 +1175,10 @@ int dap_chain_ledger_token_emission_add_check(dap_ledger_t *a_ledger, const dap_
                                 for(uint16_t k=0; k< l_token_item->auth_signs_total; k++  ){
                                     if ( dap_hash_fast_compare(&l_sign_pkey_hash, &l_token_item->auth_signs_pkey_hash[k])) {
                                         // Verify if its token emission header signed
-                                        if (!dap_sign_verify_size(l_sign, a_token_emission_size)) {
+                                        if (!dap_sign_verify_size(l_sign, l_emission_size)) {
                                             break;
                                         }
-                                        if( dap_sign_verify(l_sign,&a_token_emission->hdr, sizeof (a_token_emission) ) ){
+                                        if (dap_sign_verify(l_sign, &l_emission->hdr, sizeof(l_emission))) {
                                             l_aproves++;
                                             break;
                                         }
@@ -1188,18 +1192,19 @@ int dap_chain_ledger_token_emission_add_check(dap_ledger_t *a_ledger, const dap_
                         if (l_aproves < l_aproves_valid ){
                             if(s_debug_more)
                                 log_it(L_WARNING, "Emission of %"DAP_UINT64_FORMAT_U" datoshi of %s:%s is wrong: only %u valid aproves when %u need",
-                                   a_token_emission->hdr.value, a_ledger->net_name, a_token_emission->hdr.ticker, l_aproves, l_aproves_valid );
+                                   l_emission->hdr.value, a_ledger->net_name, l_emission->hdr.ticker, l_aproves, l_aproves_valid );
                             ret = -1;
                         }
                     }
                 }else{
                     if(s_debug_more)
-                        log_it(L_WARNING,"Can't find token declaration %s:%s thats pointed in token emission datum", a_ledger->net_name, a_token_emission->hdr.ticker);
+                        log_it(L_WARNING,"Can't find token declaration %s:%s thats pointed in token emission datum", a_ledger->net_name, l_emission->hdr.ticker);
                     ret = DAP_CHAIN_CS_VERIFY_CODE_TX_NO_PREVIOUS;
                 }
             }break;
             default:{}
         }
+        DAP_DELETE(l_emission);
     }
     DAP_DELETE(l_hash_str);
 
@@ -1212,21 +1217,19 @@ int dap_chain_ledger_token_emission_add_check(dap_ledger_t *a_ledger, const dap_
  * @param a_token_emision_size
  * @return
  */
-int dap_chain_ledger_token_emission_add(dap_ledger_t *a_ledger,
-        const dap_chain_datum_token_emission_t *a_token_emission, size_t a_token_emission_size)
+int dap_chain_ledger_token_emission_add(dap_ledger_t *a_ledger, byte_t *a_token_emission, size_t a_token_emission_size)
 {
     int ret = 0;
     dap_ledger_private_t *l_ledger_priv = PVT(a_ledger);
 
-    const char * c_token_ticker = a_token_emission->hdr.ticker;
+    const char * c_token_ticker = ((dap_chain_datum_token_emission_t *)a_token_emission)->hdr.ticker;
     dap_chain_ledger_token_item_t * l_token_item = NULL;
     pthread_rwlock_rdlock(&l_ledger_priv->tokens_rwlock);
     HASH_FIND_STR(l_ledger_priv->tokens, c_token_ticker, l_token_item);
     pthread_rwlock_unlock(&l_ledger_priv->tokens_rwlock);
     dap_chain_ledger_token_emission_item_t * l_token_emission_item = NULL;
     // check if such emission is already present in table
-    dap_chain_hash_fast_t l_token_emission_hash={0};
-    dap_chain_hash_fast_t * l_token_emission_hash_ptr = &l_token_emission_hash;
+    dap_chain_hash_fast_t l_token_emission_hash = {};
     dap_hash_fast(a_token_emission, a_token_emission_size, &l_token_emission_hash);
     char * l_hash_str = dap_chain_hash_fast_to_str_new(&l_token_emission_hash);
     pthread_rwlock_rdlock( l_token_item ? &l_token_item->token_emissions_rwlock
@@ -1239,11 +1242,13 @@ int dap_chain_ledger_token_emission_add(dap_ledger_t *a_ledger,
     if(l_token_emission_item == NULL ) {
         if ( l_token_item || l_threshold_emissions_count < s_treshold_emissions_max  ) {
             l_token_emission_item = DAP_NEW_Z(dap_chain_ledger_token_emission_item_t);
-            l_token_emission_item->datum_token_emission =
-                    DAP_NEW_Z_SIZE(dap_chain_datum_token_emission_t, a_token_emission_size);
+            size_t l_emission_size = a_token_emission_size;
+            l_token_emission_item->datum_token_emission = l_token_item
+                                                                     ? dap_chain_datum_emission_read(a_token_emission, &l_emission_size)
+                                                                     : DAP_DUP_SIZE(a_token_emission, a_token_emission_size);
             memcpy(l_token_emission_item->datum_token_emission, a_token_emission, a_token_emission_size);
             memcpy(&l_token_emission_item->datum_token_emission_hash,
-                    l_token_emission_hash_ptr, sizeof(l_token_emission_hash));
+                   &l_token_emission_hash, sizeof(l_token_emission_hash));
             l_token_emission_item->datum_token_emission_size = a_token_emission_size;
             pthread_rwlock_wrlock( l_token_item ? &l_token_item->token_emissions_rwlock
                                                 : &l_ledger_priv->treshold_emissions_rwlock);
@@ -1257,20 +1262,19 @@ int dap_chain_ledger_token_emission_add(dap_ledger_t *a_ledger,
             pthread_rwlock_unlock( l_token_item ? &l_token_item->token_emissions_rwlock
                                                 : &l_ledger_priv->treshold_emissions_rwlock);
             // Add it to cache
-            dap_chain_datum_token_emission_t *l_emission_cache = DAP_NEW_Z_SIZE(dap_chain_datum_token_emission_t, a_token_emission_size);
-            memcpy(l_emission_cache, a_token_emission, a_token_emission_size);
+            dap_chain_datum_token_emission_t *l_emission_cache = DAP_DUP_SIZE(a_token_emission, a_token_emission_size);
             char *l_gdb_group = dap_chain_ledger_get_gdb_group(a_ledger, DAP_CHAIN_LEDGER_EMISSIONS_STR);
             if (!dap_chain_global_db_gr_set(dap_strdup(l_hash_str), l_emission_cache, a_token_emission_size, l_gdb_group)) {
                 log_it(L_WARNING, "Ledger cache mismatch");
                 DAP_DELETE(l_emission_cache);
             }
             DAP_DELETE(l_gdb_group);
-            char * l_token_emission_address_str = dap_chain_addr_to_str( &(a_token_emission->hdr.address) );
+            char * l_token_emission_address_str = dap_chain_addr_to_str(&(l_token_emission_item->datum_token_emission->hdr.address) );
             if(s_debug_more)
                 log_it(L_NOTICE, "Added token emission datum to %s: type=%s value=%.1Lf token=%s to_addr=%s ",
                            l_token_item?"emissions cache":"emissions treshold",
-                           c_dap_chain_datum_token_emission_type_str[ a_token_emission->hdr.type ] ,
-                           dap_chain_datoshi_to_coins(a_token_emission->hdr.value), c_token_ticker,
+                           c_dap_chain_datum_token_emission_type_str[l_token_emission_item->datum_token_emission->hdr.type ] ,
+                           dap_chain_datoshi_to_coins(l_token_emission_item->datum_token_emission->hdr.value), c_token_ticker,
                            l_token_emission_address_str);
             DAP_DELETE(l_token_emission_address_str);
         }else{
@@ -1283,7 +1287,8 @@ int dap_chain_ledger_token_emission_add(dap_ledger_t *a_ledger,
         if (l_token_item) {
             if(s_debug_more)
                 log_it(L_ERROR, "Duplicate token emission datum of %"DAP_UINT64_FORMAT_U" %s ( %s )",
-                            a_token_emission->hdr.value, c_token_ticker, l_hash_str);
+                                ((dap_chain_datum_token_emission_t *)a_token_emission)->hdr.value,
+                                c_token_ticker, l_hash_str);
         }
         ret = -1;
     }
@@ -1291,7 +1296,7 @@ int dap_chain_ledger_token_emission_add(dap_ledger_t *a_ledger,
     return ret;
 }
 
-int dap_chain_ledger_token_emission_load(dap_ledger_t *a_ledger, const dap_chain_datum_token_emission_t *a_token_emission, size_t a_token_emission_size)
+int dap_chain_ledger_token_emission_load(dap_ledger_t *a_ledger, byte_t *a_token_emission, size_t a_token_emission_size)
 {
     if (PVT(a_ledger)->last_emit.found) {
         return dap_chain_ledger_token_emission_add(a_ledger, a_token_emission, a_token_emission_size);
@@ -1767,24 +1772,26 @@ int dap_chain_ledger_tx_cache_check(dap_ledger_t *a_ledger, dap_chain_datum_tx_t
             size_t l_pkey_ser_size = 0;
             const uint8_t *l_pkey_ser = dap_sign_get_pkey(l_sign, &l_pkey_ser_size);
             dap_chain_tx_out_cond_t *l_tx_prev_out_cond = (dap_chain_tx_out_cond_t *)l_tx_prev_out;
-            if (l_pkey_ser_size != l_prev_pkey_ser_size ||
-                    memcmp(l_prev_pkey_ser, l_pkey_ser, l_prev_pkey_ser_size)) {
-                // 5b. Call verificator for conditional output
-                dap_chain_ledger_verificator_t *l_verificator;
-                int l_tmp = (int)l_tx_prev_out_cond->header.subtype;
-                pthread_rwlock_rdlock(&s_verificators_rwlock);
-                HASH_FIND_INT(s_verificators, &l_tmp, l_verificator);
-                pthread_rwlock_unlock(&s_verificators_rwlock);
-                if (!l_verificator || !l_verificator->callback) {
-                    if(s_debug_more)
-                        log_it(L_ERROR, "No verificator set for conditional output subtype %d", l_tmp);
-                    l_err_num = -13;
-                    break;
-                }
-                if (l_verificator->callback(l_tx_prev_out_cond, a_tx) == false) {
-                    l_err_num = -14;
-                    break;
-                }
+            bool l_owner = false;
+            if (l_pkey_ser_size == l_prev_pkey_ser_size &&
+                    !memcmp(l_prev_pkey_ser, l_pkey_ser, l_prev_pkey_ser_size)) {
+                l_owner = true;
+            }
+            // 5b. Call verificator for conditional output
+            dap_chain_ledger_verificator_t *l_verificator;
+            int l_tmp = (int)l_tx_prev_out_cond->header.subtype;
+            pthread_rwlock_rdlock(&s_verificators_rwlock);
+            HASH_FIND_INT(s_verificators, &l_tmp, l_verificator);
+            pthread_rwlock_unlock(&s_verificators_rwlock);
+            if (!l_verificator || !l_verificator->callback) {
+                if(s_debug_more)
+                    log_it(L_ERROR, "No verificator set for conditional output subtype %d", l_tmp);
+                l_err_num = -13;
+                break;
+            }
+            if (l_verificator->callback(l_tx_prev_out_cond, a_tx, l_owner) == false) {
+                l_err_num = -14;
+                break;
             }
             bound_item->out.tx_prev_out_cond = l_tx_prev_out_cond;
             // calculate sum of values from previous transactions
@@ -2170,6 +2177,7 @@ int dap_chain_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx,
     // find all bound pairs 'in' and 'out'
     dap_list_t *l_list_tmp = l_list_bound_items;
     char *l_ticker_trl = NULL, *l_ticker_old_trl = NULL;
+    bool l_stake_updated = false;
     // Update balance: deducts
     while(l_list_tmp) {
         dap_chain_ledger_tx_bound_t *bound_item = l_list_tmp->data;
@@ -2220,6 +2228,19 @@ int dap_chain_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx,
             dap_chain_tx_in_cond_t *l_tx_in_cond = bound_item->in.tx_cur_in_cond;
             /// Mark 'out' item in cache because it used
             l_tx_prev_out_used_idx = l_tx_in_cond->header.tx_out_prev_idx;
+            // Update stakes if any
+            dap_chain_tx_out_cond_t *l_cond = bound_item->out.tx_prev_out_cond;
+            if (l_cond->header.subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE) {
+                dap_chain_ledger_verificator_t *l_verificator;
+                int l_tmp = (int)DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_UPDATE;
+                pthread_rwlock_rdlock(&s_verificators_rwlock);
+                HASH_FIND_INT(s_verificators, &l_tmp, l_verificator);
+                pthread_rwlock_unlock(&s_verificators_rwlock);
+                if (l_verificator && l_verificator->callback) {
+                    l_verificator->callback(l_cond, a_tx, true);
+                }
+                l_stake_updated = true;
+            }
         }
         // add a used output
         memcpy(&(l_prev_item_out->cache_data.tx_hash_spent_fast[l_tx_prev_out_used_idx]), l_tx_hash, sizeof(dap_chain_hash_fast_t));
@@ -2274,16 +2295,28 @@ int dap_chain_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx,
     for (dap_list_t *l_tx_out = l_list_tx_out; l_tx_out; l_tx_out = dap_list_next(l_tx_out)) {
         dap_chain_tx_item_type_t l_type = *(uint8_t *)l_tx_out->data;
         if (l_type == TX_ITEM_TYPE_OUT_COND) {
+            // Update stakes if any
+            dap_chain_tx_out_cond_t *l_cond = (dap_chain_tx_out_cond_t *)l_tx_out->data;
+            if (l_cond->header.subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE && !l_stake_updated) {
+                dap_chain_ledger_verificator_t *l_verificator;
+                int l_tmp = (int)DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_UPDATE;
+                pthread_rwlock_rdlock(&s_verificators_rwlock);
+                HASH_FIND_INT(s_verificators, &l_tmp, l_verificator);
+                pthread_rwlock_unlock(&s_verificators_rwlock);
+                if (l_verificator && l_verificator->callback) {
+                    l_verificator->callback(NULL, a_tx, true);
+                }
+            }
             continue;   // balance raise will be with next conditional transaction
         }
         dap_chain_tx_out_t *l_out_item = NULL;
         dap_chain_tx_out_ext_t *l_out_item_ext = NULL;
         if (l_type == TX_ITEM_TYPE_OUT) {
-            l_out_item = l_tx_out->data;
+            l_out_item = (dap_chain_tx_out_t *)l_tx_out->data;
         } else {
-            l_out_item_ext = l_tx_out->data;
+            l_out_item_ext = (dap_chain_tx_out_ext_t *)l_tx_out->data;
         }
-        if (l_out_item && l_ticker_trl) {
+        if ((l_out_item  || l_out_item_ext) && l_ticker_trl) {
              dap_chain_addr_t *l_addr = (l_type == TX_ITEM_TYPE_OUT) ?
                                         &l_out_item->addr :
                                         &l_out_item_ext->addr;
diff --git a/modules/chain/include/dap_chain_ledger.h b/modules/chain/include/dap_chain_ledger.h
index 31baa98f0f..524d3bc49b 100644
--- a/modules/chain/include/dap_chain_ledger.h
+++ b/modules/chain/include/dap_chain_ledger.h
@@ -42,7 +42,7 @@ typedef struct dap_ledger {
     void *_internal;
 } dap_ledger_t;
 
-typedef bool (* dap_chain_ledger_verificator_callback_t)(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx);
+typedef bool (* dap_chain_ledger_verificator_callback_t)(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx, bool a_owner);
 
 // Checks the emission of the token, usualy on zero chain
 #define DAP_CHAIN_LEDGER_CHECK_TOKEN_EMISSION    0x0001
@@ -127,16 +127,13 @@ dap_list_t *dap_chain_ledger_token_info(dap_ledger_t *a_ledger);
 /**
  * Add token emission datum
  */
-int dap_chain_ledger_token_emission_add(dap_ledger_t *a_ledger,
-        const dap_chain_datum_token_emission_t *a_token_emission, size_t a_token_emission_size);
-int dap_chain_ledger_token_emission_load(dap_ledger_t *a_ledger,
-        const dap_chain_datum_token_emission_t *a_token_emission, size_t a_token_emission_size);
+int dap_chain_ledger_token_emission_add(dap_ledger_t *a_ledger, byte_t *a_token_emission, size_t a_token_emission_size);
+int dap_chain_ledger_token_emission_load(dap_ledger_t *a_ledger, byte_t *a_token_emission, size_t a_token_emission_size);
 
 // Check if it addable
-int dap_chain_ledger_token_emission_add_check(dap_ledger_t *a_ledger,
-        const dap_chain_datum_token_emission_t *a_token_emission, size_t a_token_emission_size);
+int dap_chain_ledger_token_emission_add_check(dap_ledger_t *a_ledger, byte_t *a_token_emission, size_t a_token_emission_size);
 
-dap_chain_datum_token_emission_t * dap_chain_ledger_token_emission_find(dap_ledger_t *a_ledger,
+dap_chain_datum_token_emission_t *dap_chain_ledger_token_emission_find(dap_ledger_t *a_ledger,
         const char *a_token_ticker, const dap_chain_hash_fast_t *a_token_emission_hash);
 
 const char* dap_chain_ledger_tx_get_token_ticker_by_hash(dap_ledger_t *a_ledger,dap_chain_hash_fast_t *a_tx_hash);
diff --git a/modules/common/dap_chain_datum_token.c b/modules/common/dap_chain_datum_token.c
index 74245df3bd..3187d5384b 100644
--- a/modules/common/dap_chain_datum_token.c
+++ b/modules/common/dap_chain_datum_token.c
@@ -201,3 +201,57 @@ err:
     return NULL;
 
 }
+
+size_t dap_chain_datum_emission_get_size(uint8_t *a_emission_serial)
+{
+    size_t l_ret = 0;
+    dap_chain_datum_token_emission_t *l_emission = (dap_chain_datum_token_emission_t *)a_emission_serial;
+    if (l_emission->hdr.version == 0) {
+        l_ret = sizeof(struct dap_chain_emission_header_v0);
+    } 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;
+    }
+    return l_ret;
+}
+
+dap_chain_datum_token_emission_t *dap_chain_datum_emission_read(uint8_t *a_emission_serial, size_t *a_emission_size)
+{
+    assert(a_emission_serial);
+    assert(a_emission_size);
+    dap_chain_datum_token_emission_t *l_emission;
+    if (((dap_chain_datum_token_emission_t *)a_emission_serial)->hdr.version == 0) {
+        size_t l_add_size = DAP_CHAIN_DATUM_NONCE_SIZE + sizeof(uint256_t) - sizeof(uint64_t);
+        l_emission = DAP_NEW_Z_SIZE(dap_chain_datum_token_emission_t, *a_emission_size + l_add_size);
+        size_t l_old_hdr_size = sizeof(struct dap_chain_emission_header_v0);
+        l_emission->hdr.version = 1;
+        memcpy(l_emission, a_emission_serial, l_old_hdr_size);
+        memcpy(l_emission + l_old_hdr_size + l_add_size,
+               a_emission_serial + l_old_hdr_size,
+               *a_emission_size - l_old_hdr_size);
+        *a_emission_size += l_add_size;
+    } else
+        l_emission = DAP_DUP_SIZE(a_emission_serial, *a_emission_size);
+    return l_emission;
+}
diff --git a/modules/common/dap_chain_datum_tx_items.c b/modules/common/dap_chain_datum_tx_items.c
index 96f0ae56df..38cde942ce 100644
--- a/modules/common/dap_chain_datum_tx_items.c
+++ b/modules/common/dap_chain_datum_tx_items.c
@@ -277,7 +277,8 @@ dap_chain_tx_out_cond_t *dap_chain_datum_tx_item_out_cond_create_srv_xchange(dap
 }
 
 dap_chain_tx_out_cond_t *dap_chain_datum_tx_item_out_cond_create_srv_stake(dap_chain_net_srv_uid_t a_srv_uid, uint64_t a_value, long double a_fee_value,
-                                                                           dap_chain_addr_t *a_fee_addr, const void *a_params, uint32_t a_params_size)
+                                                                           dap_chain_addr_t *a_fee_addr, dap_chain_addr_t *a_hldr_addr,
+                                                                           const void *a_params, uint32_t a_params_size)
 {
     dap_chain_tx_out_cond_t *l_item = DAP_NEW_Z_SIZE(dap_chain_tx_out_cond_t, sizeof(dap_chain_tx_out_cond_t) + a_params_size);
     l_item->header.item_type = TX_ITEM_TYPE_OUT_COND;
@@ -286,6 +287,7 @@ dap_chain_tx_out_cond_t *dap_chain_datum_tx_item_out_cond_create_srv_stake(dap_c
     l_item->subtype.srv_stake.srv_uid = a_srv_uid;
     l_item->subtype.srv_stake.fee_value = a_fee_value;
     memcpy(&l_item->subtype.srv_stake.fee_addr, a_fee_addr, sizeof(dap_chain_addr_t));
+    memcpy(&l_item->subtype.srv_stake.hldr_addr, a_hldr_addr, sizeof(dap_chain_addr_t));
     l_item->params_size = a_params_size;
     if (a_params_size) {
         memcpy(l_item->params, a_params, a_params_size);
diff --git a/modules/common/include/dap_chain_datum_token.h b/modules/common/include/dap_chain_datum_token.h
index d452fb9ac1..a07829a434 100644
--- a/modules/common/include/dap_chain_datum_token.h
+++ b/modules/common/include/dap_chain_datum_token.h
@@ -195,6 +195,15 @@ static inline uint16_t dap_chain_datum_token_flag_from_str(const char* a_str)
 #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_BLOCKED_REMOVE       0x0024
 #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_BLOCKED_CLEAR        0x0025
 
+#define DAP_CHAIN_DATUM_NONCE_SIZE                                    64
+
+struct DAP_ALIGN_PACKED dap_chain_emission_header_v0 {
+    uint8_t version;
+    uint8_t type; // Emission Type
+    char ticker[DAP_CHAIN_TICKER_SIZE_MAX];
+    dap_chain_addr_t address; // Emission holder's address
+    uint64_t value;
+};
 
 // Token emission
 typedef struct dap_chain_datum_token_emission{
@@ -203,7 +212,11 @@ typedef struct dap_chain_datum_token_emission{
         uint8_t type; // Emission Type
         char ticker[DAP_CHAIN_TICKER_SIZE_MAX];
         dap_chain_addr_t address; // Emission holder's address
-        uint64_t value;
+        union {
+            uint64_t value;
+            uint256_t value256;
+        };
+        uint8_t nonce[DAP_CHAIN_DATUM_NONCE_SIZE];
     } DAP_ALIGN_PACKED hdr;
     union {
         struct {
@@ -240,3 +253,5 @@ dap_tsd_t* dap_chain_datum_token_tsd_get(dap_chain_datum_token_t * a_token,  siz
 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_emission_t *dap_chain_datum_emission_read(uint8_t *a_emission_serial, size_t *a_emission_size);
+size_t dap_chain_datum_emission_get_size(uint8_t *a_emission_serial);
diff --git a/modules/common/include/dap_chain_datum_tx_items.h b/modules/common/include/dap_chain_datum_tx_items.h
index e194c7c336..3b891bc45c 100644
--- a/modules/common/include/dap_chain_datum_tx_items.h
+++ b/modules/common/include/dap_chain_datum_tx_items.h
@@ -129,7 +129,8 @@ dap_chain_tx_out_cond_t* dap_chain_datum_tx_item_out_cond_create_srv_xchange(dap
  * return item, NULL Error
  */
 dap_chain_tx_out_cond_t *dap_chain_datum_tx_item_out_cond_create_srv_stake(dap_chain_net_srv_uid_t a_srv_uid, uint64_t a_value, long double a_fee_value,
-                                                                           dap_chain_addr_t *a_fee_addr, const void *a_params, uint32_t a_params_size);
+                                                                           dap_chain_addr_t *a_fee_addr, dap_chain_addr_t *a_hldr_addr,
+                                                                           const void *a_params, uint32_t a_params_size);
 /**
  * Create item dap_chain_tx_sig_t
  *
diff --git a/modules/common/include/dap_chain_datum_tx_out_cond.h b/modules/common/include/dap_chain_datum_tx_out_cond.h
index b0b099572a..3d96e7faf7 100644
--- a/modules/common/include/dap_chain_datum_tx_out_cond.h
+++ b/modules/common/include/dap_chain_datum_tx_out_cond.h
@@ -32,7 +32,8 @@
 typedef enum dap_chain_tx_out_cond_subtype {
     DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY = 0x01,
     DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_XCHANGE = 0x02,
-    DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE = 0x03
+    DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE = 0x13,
+    DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_UPDATE = 0xFA       // Virtual type for stake update verificator
 } dap_chain_tx_out_cond_subtype_t;
 
 DAP_STATIC_INLINE const char *dap_chain_tx_out_cond_subtype_to_str(dap_chain_tx_out_cond_subtype_t a_subtype){
@@ -85,6 +86,8 @@ typedef struct dap_chain_tx_out_cond {
         struct {
             // Service uid that only could be used for this outout
             dap_chain_net_srv_uid_t srv_uid;
+            // Stake holder address
+            dap_chain_addr_t hldr_addr;
             // Fee address
             dap_chain_addr_t fee_addr;
             // Fee value in percent
diff --git a/modules/consensus/block-poa/dap_chain_cs_block_poa.c b/modules/consensus/block-poa/dap_chain_cs_block_poa.c
index dc7f7b131e..aa93918c49 100644
--- a/modules/consensus/block-poa/dap_chain_cs_block_poa.c
+++ b/modules/consensus/block-poa/dap_chain_cs_block_poa.c
@@ -27,6 +27,7 @@
 #include "dap_enc_base58.h"
 #include "dap_cert.h"
 #include "dap_chain.h"
+#include "dap_chain_pvt.h"
 #include "dap_chain_block.h"
 #include "dap_chain_block_cache.h"
 #include "dap_chain_cs_blocks.h"
@@ -340,3 +341,13 @@ static int s_callback_block_verify(dap_chain_cs_blocks_t * a_blocks, dap_chain_b
     return l_signs_verified_count >= l_poa_pvt->auth_certs_count_verify ? 0 : -1;
 }
 
+dap_cert_t **dap_chain_cs_block_poa_get_auth_certs(dap_chain_t *a_chain, size_t *a_auth_certs_count)
+{
+    dap_chain_pvt_t *l_chain_pvt = DAP_CHAIN_PVT(a_chain);
+    if (strcmp(l_chain_pvt->cs_name, "block_poa"))
+        return NULL;
+    dap_chain_cs_block_poa_pvt_t *l_poa_pvt = PVT(DAP_CHAIN_CS_BLOCK_POA(DAP_CHAIN_CS_BLOCKS(a_chain)));
+    if (a_auth_certs_count)
+        *a_auth_certs_count = l_poa_pvt->auth_certs_count;
+    return l_poa_pvt->auth_certs;
+}
diff --git a/modules/consensus/block-poa/include/dap_chain_cs_block_poa.h b/modules/consensus/block-poa/include/dap_chain_cs_block_poa.h
index e2bc7b718b..2ccd4f7025 100644
--- a/modules/consensus/block-poa/include/dap_chain_cs_block_poa.h
+++ b/modules/consensus/block-poa/include/dap_chain_cs_block_poa.h
@@ -38,3 +38,4 @@ typedef struct dap_chain_cs_block_poa
 
 int dap_chain_cs_block_poa_init(void);
 void dap_chain_cs_block_poa_deinit(void);
+dap_cert_t **dap_chain_cs_block_poa_get_auth_certs(dap_chain_t *a_chain, size_t *a_auth_certs_count);
diff --git a/modules/consensus/block-pos/dap_chain_cs_block_pos.c b/modules/consensus/block-pos/dap_chain_cs_block_pos.c
index 7b6d0dfc3b..90ffe94de3 100644
--- a/modules/consensus/block-pos/dap_chain_cs_block_pos.c
+++ b/modules/consensus/block-pos/dap_chain_cs_block_pos.c
@@ -252,44 +252,26 @@ static int s_callback_block_verify(dap_chain_cs_blocks_t *a_blocks, dap_chain_bl
             return -41;
         }
 
-        /*dap_chain_hash_fast_t l_pkey_hash;
-        if (!dap_sign_get_pkey_hash(l_sign, &l_pkey_hash)) {
-            log_it(L_WARNING, "Block's sign has no any key");
-            return -5;
-        }
-        dap_chain_addr_t l_addr = {};
-        dap_chain_addr_fill(&l_addr, l_sign->header.type, &l_pkey_hash, a_blocks->chain->net_id);
-
         if (l_sig_pos == 0) {
+            dap_chain_addr_t l_addr = {};
+            dap_chain_hash_fast_t l_pkey_hash;
+            dap_sign_get_pkey_hash(l_sign, &l_pkey_hash);
+            dap_chain_addr_fill(&l_addr, l_sign->header.type, &l_pkey_hash, a_blocks->chain->net_id);
             size_t l_datums_count = 0;
             dap_chain_datum_t **l_datums = dap_chain_block_get_datums(a_block, a_block_size, &l_datums_count);
-            for (unsigned i = 0; i < l_datums_count; i++) {
-                if (l_datums[i]->header.type_id == DAP_CHAIN_DATUM_TX) {
-                    dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t *)l_datums[i]->data;
-                    if (!dap_chain_net_srv_stake_validator(&l_addr, l_tx)) {
-                        log_it(L_WARNING,"Not passed stake validator with datum %u of block %p on chain %s", i, a_block, a_blocks->chain->name);
-                        return -6;
-                    }
-                }
+            if (!l_datums || !l_datums_count) {
+                log_it(L_WARNING, "No datums in block %p on chain %s", a_block, a_blocks->chain->name);
+                return -7;
             }
-        }
-
-        bool l_is_enough_balance = false;
-        for (size_t i = 0; i < l_pos_pvt->tokens_hold_size; i++) {
-            uint128_t l_balance = dap_chain_ledger_calc_balance(a_blocks->chain->ledger, &l_addr, l_pos_pvt->tokens_hold[i]);
-            uint64_t l_value = dap_chain_uint128_to(l_balance);
-            if (l_value >= l_pos_pvt->tokens_hold_value[i]) {
-                l_verified_num++;
-                l_is_enough_balance = true;
-                break;
+            for (size_t i = 0; i < l_datums_count; i++) {
+                if (!dap_chain_net_srv_stake_validator(&l_addr, l_datums[i])) {
+                    log_it(L_WARNING, "Not passed stake validator datum %zu with block %p on chain %s", i, a_block, a_blocks->chain->name);
+                    DAP_DELETE(l_datums);
+                    return -6;
+                }
             }
+            DAP_DELETE(l_datums);
         }
-        if (!l_is_enough_balance) {
-            char *l_addr_str = dap_chain_addr_to_str(&l_addr);
-            log_it(L_WARNING, "Verify of block is false, because balance is not enough for addr=%s", l_addr_str);
-            DAP_DELETE(l_addr_str);
-            return -1;
-        }*/ // TODO comlete stake validator for all datums in the block
     }
 
     // Check number
diff --git a/modules/consensus/dag-poa/dap_chain_cs_dag_poa.c b/modules/consensus/dag-poa/dap_chain_cs_dag_poa.c
index b759fddcec..ef2ef81218 100644
--- a/modules/consensus/dag-poa/dap_chain_cs_dag_poa.c
+++ b/modules/consensus/dag-poa/dap_chain_cs_dag_poa.c
@@ -36,6 +36,7 @@
 #include "dap_common.h"
 #include "dap_strfuncs.h"
 #include "dap_enc_base58.h"
+#include "dap_chain_pvt.h"
 #include "dap_chain_net.h"
 #include "dap_chain_node_cli.h"
 #include "dap_chain_node_cli_cmd.h"
@@ -402,24 +403,6 @@ static int s_callback_event_verify(dap_chain_cs_dag_t * a_dag, dap_chain_cs_dag_
                 if (dap_cert_compare_with_sign ( l_poa_pvt->auth_certs[j], l_sign) == 0)
                     l_verified++;
             }
-            if (i == 0) {
-                dap_chain_hash_fast_t l_pkey_hash;
-                if (!dap_sign_get_pkey_hash(l_sign, &l_pkey_hash)) {
-                    log_it(L_WARNING, "Event's sign has no any key");
-                    return -5;
-                }
-                dap_chain_addr_t l_addr = {};
-                dap_chain_addr_fill(&l_addr, l_sign->header.type, &l_pkey_hash, a_dag->chain->net_id);
-                dap_chain_datum_t *l_datum = (dap_chain_datum_t *)dap_chain_cs_dag_event_get_datum(a_dag_event, a_dag_event_size);
-                if (l_datum->header.type_id == DAP_CHAIN_DATUM_TX) {
-                    dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t *)l_datum->data;
-                    if (!dap_chain_net_srv_stake_validator(&l_addr, l_tx)) {
-                        log_it(L_WARNING,"Not passed stake validator event %p", a_dag_event);
-                        return -6;
-                    }
-                }
-            }
-
         }
         return l_verified >= l_poa_pvt->auth_certs_count_verify ? 0 : -1;
     }else if (a_dag_event->header.hash_count == 0){
@@ -437,3 +420,13 @@ static int s_callback_event_verify(dap_chain_cs_dag_t * a_dag, dap_chain_cs_dag_
     }
 }
 
+dap_cert_t **dap_chain_cs_dag_poa_get_auth_certs(dap_chain_t *a_chain, size_t *a_auth_certs_count)
+{
+    dap_chain_pvt_t *l_chain_pvt = DAP_CHAIN_PVT(a_chain);
+    if (strcmp(l_chain_pvt->cs_name, "dag_poa"))
+        return NULL;
+    dap_chain_cs_dag_poa_pvt_t *l_poa_pvt = PVT(DAP_CHAIN_CS_DAG_POA(DAP_CHAIN_CS_DAG(a_chain)));
+    if (a_auth_certs_count)
+        *a_auth_certs_count = l_poa_pvt->auth_certs_count;
+    return l_poa_pvt->auth_certs;
+}
diff --git a/modules/consensus/dag-poa/include/dap_chain_cs_dag_poa.h b/modules/consensus/dag-poa/include/dap_chain_cs_dag_poa.h
index b6f05be820..c437ff91d2 100644
--- a/modules/consensus/dag-poa/include/dap_chain_cs_dag_poa.h
+++ b/modules/consensus/dag-poa/include/dap_chain_cs_dag_poa.h
@@ -23,6 +23,7 @@
 */
 #pragma once
 #include "dap_chain_cs_dag.h"
+#include "dap_cert.h"
 
 typedef struct dap_chain_cs_dag_poa
 {
@@ -37,3 +38,4 @@ typedef struct dap_chain_cs_dag_poa
 
 int dap_chain_cs_dag_poa_init(void);
 void dap_chain_cs_dag_poa_deinit(void);
+dap_cert_t **dap_chain_cs_dag_poa_get_auth_certs(dap_chain_t *a_chain, size_t *a_auth_certs_count);
diff --git a/modules/consensus/dag-pos/dap_chain_cs_dag_pos.c b/modules/consensus/dag-pos/dap_chain_cs_dag_pos.c
index 2641129fd3..367d76b0e5 100644
--- a/modules/consensus/dag-pos/dap_chain_cs_dag_pos.c
+++ b/modules/consensus/dag-pos/dap_chain_cs_dag_pos.c
@@ -234,7 +234,6 @@ static int s_callback_event_verify(dap_chain_cs_dag_t * a_dag, dap_chain_cs_dag_
     }
     if ( a_dag_event->header.signs_count >= l_pos_pvt->confirmations_minimum ){
         uint16_t l_verified_num = 0;
-        dap_chain_addr_t l_addr = { 0 };
 
         for ( size_t l_sig_pos=0; l_sig_pos < a_dag_event->header.signs_count; l_sig_pos++ ){
             dap_sign_t * l_sign = dap_chain_cs_dag_event_get_sign(a_dag_event, a_dag_event_size,l_sig_pos);
@@ -258,65 +257,18 @@ static int s_callback_event_verify(dap_chain_cs_dag_t * a_dag, dap_chain_cs_dag_
                 return -41;
             }
 
-            if (!l_dag_event_size_without_sign){
-                log_it(L_WARNING,"Event has nothing except sign, nothing to verify so I pass it (who knows why we have it?)");
-                return 0;
-            }
-
-            dap_chain_hash_fast_t l_pkey_hash;
-            if (!dap_sign_get_pkey_hash(l_sign, &l_pkey_hash)) {
-                log_it(L_WARNING, "Event's sign has no any key");
-                return -5;
-            }
-            dap_chain_addr_fill(&l_addr, l_sign->header.type, &l_pkey_hash, a_dag->chain->net_id);
-
-
-
             if (l_sig_pos == 0) {
-                dap_chain_datum_t *l_datum = (dap_chain_datum_t *)dap_chain_cs_dag_event_get_datum(a_dag_event,a_dag_event_size);
-                if (l_datum->header.type_id == DAP_CHAIN_DATUM_TX) {
-                    dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t *)l_datum->data;
-                    if (!dap_chain_net_srv_stake_validator(&l_addr, l_tx)) {
-                        log_it(L_WARNING,"Not passed stake validator with event %p on chain %s", a_dag_event, a_dag->chain->name);
-                        return -6;
-                    }
+                dap_chain_addr_t l_addr = {};
+                dap_chain_hash_fast_t l_pkey_hash;
+                dap_sign_get_pkey_hash(l_sign, &l_pkey_hash);
+                dap_chain_addr_fill(&l_addr, l_sign->header.type, &l_pkey_hash, a_dag->chain->net_id);
+                dap_chain_datum_t *l_datum = (dap_chain_datum_t *)dap_chain_cs_dag_event_get_datum(a_dag_event, a_dag_event_size);
+                if (!dap_chain_net_srv_stake_validator(&l_addr, l_datum)) {
+                    log_it(L_WARNING, "Not passed stake validator with event %p on chain %s", a_dag_event, a_dag->chain->name);
+                    return -6;
                 }
             }
-            /*
-            dap_chain_datum_t *l_datum = dap_chain_cs_dag_event_get_datum(a_dag_event);
-            // transaction include emission?
-            bool l_is_emit = false;
-            if(l_datum && l_datum->header.type_id == DAP_CHAIN_DATUM_TX) {
-                // transaction
-                dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*) l_datum->data;
-                // find Token items
-                dap_list_t *l_list_tx_token = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_TOKEN, NULL);
-                if(l_list_tx_token)
-                    l_is_emit = true;
-                dap_list_free(l_list_tx_token);
-            }
-            // if emission then the wallet can be with zero balance
-            if(l_is_emit)
-                return 0;*/
-
-            bool l_is_enough_balance = false;
-            for (size_t i =0; i <l_pos_pvt->tokens_hold_size; i++){
-                uint128_t l_balance = dap_chain_ledger_calc_balance ( a_dag->chain->ledger , &l_addr, l_pos_pvt->tokens_hold[i] );
-                uint64_t l_value = dap_chain_uint128_to(l_balance);
-                if (l_value >= l_pos_pvt->tokens_hold_value[i]) {
-                    l_verified_num++;
-                    l_is_enough_balance = true;
-                    break;
-                }
-            }
-            if (! l_is_enough_balance ){
-                char *l_addr_str = dap_chain_addr_to_str(&l_addr);
-                log_it(L_WARNING, "Verify of event is false, because bal is not enough for addr=%s", l_addr_str);
-                DAP_DELETE(l_addr_str);
-                return -1;
-            }
         }
-
         // Check number
         if ( l_verified_num >= l_pos_pvt->confirmations_minimum ){
             // Passed all checks
diff --git a/modules/consensus/none/dap_chain_cs_none.c b/modules/consensus/none/dap_chain_cs_none.c
index 346a5c2f84..233377e230 100644
--- a/modules/consensus/none/dap_chain_cs_none.c
+++ b/modules/consensus/none/dap_chain_cs_none.c
@@ -322,8 +322,7 @@ static dap_chain_atom_verify_res_t s_chain_callback_atom_add(dap_chain_t * a_cha
                 return ATOM_REJECT;
         }break;
         case DAP_CHAIN_DATUM_TOKEN_EMISSION: {
-            dap_chain_datum_token_emission_t *l_token_emission = (dap_chain_datum_token_emission_t*) l_datum->data;
-            if (dap_chain_ledger_token_emission_load(a_chain->ledger, l_token_emission, l_datum->header.data_size))
+            if (dap_chain_ledger_token_emission_load(a_chain->ledger, l_datum->data, l_datum->header.data_size))
                 return ATOM_REJECT;
         }break;
         case DAP_CHAIN_DATUM_TX:{
diff --git a/modules/net/dap_chain_net.c b/modules/net/dap_chain_net.c
index b4a5144026..ef56ed93cc 100644
--- a/modules/net/dap_chain_net.c
+++ b/modules/net/dap_chain_net.c
@@ -99,7 +99,6 @@
 #include "dap_stream_ch.h"
 #include "dap_stream_ch_pkt.h"
 #include "dap_chain_node_dns_client.h"
-
 #include "dap_module.h"
 
 #include <stdio.h>
@@ -2547,7 +2546,7 @@ int dap_chain_net_verify_datum_for_add(dap_chain_net_t *a_net, dap_chain_datum_t
         case DAP_CHAIN_DATUM_TOKEN_DECL: return dap_chain_ledger_token_decl_add_check( a_net->pub.ledger,
                                                                    (dap_chain_datum_token_t*) a_datum->data );
         case DAP_CHAIN_DATUM_TOKEN_EMISSION : return dap_chain_ledger_token_emission_add_check( a_net->pub.ledger,
-                                                                   (dap_chain_datum_token_emission_t*) a_datum->data, a_datum->header.data_size );
+                                                                    a_datum->data, a_datum->header.data_size );
         default: return 0;
     }
 }
@@ -2865,11 +2864,14 @@ void dap_chain_net_dump_datum(dap_string_t * a_str_out, dap_chain_datum_t * a_da
                                         case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE:{
                                             dap_string_append_printf(a_str_out,"\tsubtype: DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE\n");
                                         }break;
+                                        default:{
+                                            dap_string_append_printf(a_str_out,"\tsubtype: UNKNOWN\n");
+                                        }break;
                                     }
                                     dap_string_append_printf(a_str_out,"\tparams_size : %u\n", l_out->params_size );
                                 } break;
-                                case TX_ITEM_TYPE_RECEIPT:{} break;
-                                default:{}
+                                case TX_ITEM_TYPE_RECEIPT:
+                                default: break;
                             }
                             n++;
                         }
diff --git a/modules/net/dap_chain_node_cli_cmd.c b/modules/net/dap_chain_node_cli_cmd.c
index ff4ca0d020..18a5f26afc 100644
--- a/modules/net/dap_chain_node_cli_cmd.c
+++ b/modules/net/dap_chain_node_cli_cmd.c
@@ -3177,10 +3177,10 @@ int com_token_emit(int a_argc, char ** a_argv, char ** a_str_reply)
         return -43;
     }
 
-    // Wallet address that recieves the emission
+    // Token emission
     dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-emission", &l_emission_hash_str);
 
-    // Wallet address that recieves the emission
+    // Emission certs
     dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-certs", &l_certs_str);
 
     // Wallet address that recieves the emission
@@ -3189,7 +3189,7 @@ 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);
 
-    // Token emission
+    // Emission value
     if(dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-emission_value", &str_tmp)) {
         l_emission_value = strtoull(str_tmp, NULL, 10);
     }
@@ -3293,10 +3293,13 @@ int com_token_emit(int a_argc, char ** a_argv, char ** a_str_reply)
                 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 = 1;
         strncpy(l_emission->hdr.ticker, l_ticker, sizeof(l_emission->hdr.ticker) - 1);
         l_emission->hdr.value = l_emission_value;
         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));
         // Then add signs
         size_t l_offset = 0;
         for(size_t i = 0; i < l_certs_size; i++) {
@@ -3333,15 +3336,15 @@ int com_token_emit(int a_argc, char ** a_argv, char ** a_str_reply)
         if(dap_chain_global_db_gr_set(dap_strdup(l_emission_hash_str_new), (uint8_t *) l_datum_emission, l_datum_emission_size
                 , l_gdb_group_mempool_emission)) {
             if(!dap_strcmp(l_hash_out_type,"hex"))
-                str_reply_tmp = dap_strdup_printf("datum emission %s is placed in datum pool ", l_emission_hash_str_new);
+                str_reply_tmp = dap_strdup_printf("Datum emission %s is placed in datum pool", l_emission_hash_str_new);
             else
-                str_reply_tmp = dap_strdup_printf("datum emission %s is placed in datum pool ", l_emission_hash_str_base58);
+                str_reply_tmp = dap_strdup_printf("Datum emission %s is placed in datum pool", l_emission_hash_str_base58);
         }
         else {
             if(!dap_strcmp(l_hash_out_type,"hex"))
-                dap_chain_node_cli_set_reply_text(a_str_reply, "datum emission %s is not placed in datum pool ", l_emission_hash_str_new);
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Datum emission %s is not placed in datum pool", l_emission_hash_str_new);
             else
-                dap_chain_node_cli_set_reply_text(a_str_reply, "datum emission %s is not placed in datum pool ", l_emission_hash_str_base58);
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Datum emission %s is not placed in datum pool", l_emission_hash_str_base58);
             DAP_DEL_Z(l_emission_hash_str_new);
             l_emission_hash_str = NULL;
             DAP_DEL_Z(l_emission_hash_str_base58);
diff --git a/modules/net/dap_chain_node_cli_cmd_tx.c b/modules/net/dap_chain_node_cli_cmd_tx.c
index 8d4e8ed037..6fb5949d81 100644
--- a/modules/net/dap_chain_node_cli_cmd_tx.c
+++ b/modules/net/dap_chain_node_cli_cmd_tx.c
@@ -274,7 +274,7 @@ static void s_dap_chain_datum_tx_out_data(dap_chain_datum_tx_t *a_datum,
                                      ((dap_chain_tx_out_cond_t*)item)->header.value,
                                      dap_chain_tx_out_cond_subtype_to_str(((dap_chain_tx_out_cond_t*)item)->header.subtype));
             switch (((dap_chain_tx_out_cond_t*)item)->header.subtype) {
-            case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY:
+                case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY:
                 l_hash_str_tmp = dap_chain_hash_fast_to_str_new(&((dap_chain_tx_out_cond_t*)item)->subtype.srv_pay.pkey_hash);
                 dap_string_append_printf(a_str_out, "\t\t\t unit: 0x%08x\n"
                                                     "\t\t\t uid: 0x%016"DAP_UINT64_FORMAT_x"\n"
@@ -289,7 +289,7 @@ static void s_dap_chain_datum_tx_out_data(dap_chain_datum_tx_t *a_datum,
                                          ((dap_chain_tx_out_cond_t*)item)->subtype.srv_pay.unit_price_max_datoshi);
                 DAP_FREE(l_hash_str_tmp);
                 break;
-            case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE:
+                case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE:
                 dap_string_append_printf(a_str_out, "\t\t\t uid: 0x%016"DAP_UINT64_FORMAT_x"\n"
                                                     "\t\t\t addr: %s\n"
                                                     "\t\t\t value: %Lf",
@@ -299,7 +299,7 @@ static void s_dap_chain_datum_tx_out_data(dap_chain_datum_tx_t *a_datum,
                                              ),
                                          ((dap_chain_tx_out_cond_t*)item)->subtype.srv_stake.fee_value);
                 break;
-            case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_XCHANGE:
+                case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_XCHANGE:
                 dap_string_append_printf(a_str_out, "\t\t\t uid: 0x%016"DAP_UINT64_FORMAT_x"\n"
                                                     "\t\t\t net id: 0x%016"DAP_UINT64_FORMAT_x"\n"
                                                     "\t\t\t token: %s\n"
@@ -314,6 +314,7 @@ static void s_dap_chain_datum_tx_out_data(dap_chain_datum_tx_t *a_datum,
                                              ),
                                          ((dap_chain_tx_out_cond_t*)item)->subtype.srv_xchange.value);
                 break;
+                default: break;
             }
             break;
         case TX_ITEM_TYPE_OUT_EXT:
@@ -1208,7 +1209,8 @@ static char* dap_db_history_filter(dap_chain_t * a_chain, dap_ledger_t *a_ledger
                  l_token_num++;
                  break;
             }
-            dap_chain_datum_token_emission_t *l_token_em = (dap_chain_datum_token_emission_t*) l_datum->data;
+            size_t l_emission_size = dap_chain_datum_emission_get_size(l_datum->data);
+            dap_chain_datum_token_emission_t *l_token_em = dap_chain_datum_emission_read(l_datum->data, &l_emission_size);
             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
@@ -1252,10 +1254,10 @@ static char* dap_db_history_filter(dap_chain_t * a_chain, dap_ledger_t *a_ledger
                 dap_string_append_printf(l_str_out, "\n");
                 l_emission_num++;
             }
-        }
-            break;
+            DAP_DELETE(l_token_em);
+        } break;
 
-            // transaction
+        // transaction
         case DAP_CHAIN_DATUM_TX:{
 
             // datum out of page
@@ -1264,247 +1266,8 @@ static char* dap_db_history_filter(dap_chain_t * a_chain, dap_ledger_t *a_ledger
                 break;
             }
             dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*)l_datum->data;
-//            dap_chain_tx_hash_processed_ht_t *l_tx_hash_processed = a_tx_hash_processed;
             //calc tx hash
             s_dap_chain_datum_tx_out_data(l_tx, a_ledger, l_str_out, a_hash_out_type, true, &a_tx_hash_processed, &l_tx_num);
-//            a_tx_hash_processed = l_tx_hash_processed;
-//            l_tx_num++;
-
-            /*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);
-                }
-                l_list_tmp = dap_list_next(l_list_tmp);
-            }
-
-            //find SIG type
-            dap_list_t *l_list_sig_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_SIG, NULL);
-            unsigned int l_list_sig_items_len = dap_list_length(l_list_sig_items);
-            //TX_ITEM_TYPE_SIG
-            dap_string_append_printf(l_str_out, "Count SIGN: %i \n", l_list_sig_items_len);
-            l_list_tmp = l_list_sig_items;
-            while (l_list_tmp) {
-                dap_chain_tx_sig_t *l_sig_tx = (dap_chain_tx_sig_t *)l_list_tmp->data;
-                dap_chain_hash_fast_t *l_sign_hash_fast = DAP_NEW(dap_chain_hash_fast_t);
-                dap_sign_t *l_sign = dap_chain_datum_tx_item_sign_get_sig(l_sig_tx);
-                if (dap_sign_get_pkey_hash(l_sign, l_sign_hash_fast)){
-                    char l_tx_sign_hash_str[70];
-                    dap_chain_hash_fast_to_str(l_sign_hash_fast, l_tx_sign_hash_str, 70);
-                    dap_string_append_printf(l_str_out, "%s\n", l_tx_sign_hash_str);
-                }else{
-                    dap_string_append_printf(l_str_out, "Can't get pkey for sign \n");
-                }
-                DAP_FREE(l_sign_hash_fast);
-                l_list_tmp = dap_list_next(l_list_tmp);
-            }
-            dap_list_free(l_list_sig_items);
-            //find PKEY
-            dap_list_t *l_list_pkey_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_PKEY, NULL);
-            unsigned int l_list_pkey_items_len = dap_list_length(l_list_pkey_items);
-            dap_string_append_printf(l_str_out, "Count PKEY: %i \n", l_list_pkey_items_len);
-            dap_list_free(l_list_pkey_items);
-            //find TOKEN
-            dap_list_t *l_list_token_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_TOKEN, NULL);
-            unsigned int l_list_token_items_len = dap_list_length(l_list_token_items);
-            dap_string_append_printf(l_str_out, "Count TOKEN: %i \n", l_list_token_items_len);
-            l_list_tmp = l_list_token_items;
-            while(l_list_tmp){
-                dap_chain_tx_token_t *l_token = (dap_chain_tx_token_t*)l_list_tmp->data;
-                l_list_tmp = dap_list_next(l_list_tmp);
-            }
-            dap_list_free(l_list_token_items);
-            //find IN_COND
-            dap_list_t *l_list_in_cond_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN_COND, NULL);
-            unsigned int l_list_in_cond_items_len = dap_list_length(l_list_in_cond_items);
-            dap_string_append_printf(l_str_out, "Count IN_COND: %i \n", l_list_in_cond_items_len);
-            dap_list_free(l_list_in_cond_items);
-            //find OUT_COND
-            dap_list_t *l_list_out_cond_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT_COND, NULL);
-            unsigned int l_list_out_cond_items_len = dap_list_length(l_list_out_cond_items);
-            dap_string_append_printf(l_str_out, "Count OUT_COND: %i \n", l_list_out_cond_items_len);
-            dap_list_free(l_list_out_cond_items);
-            //find OUT_EXT
-            dap_list_t *l_list_out_ext_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT_EXT, NULL);
-            unsigned int l_list_out_ext_items_len = dap_list_length(l_list_out_ext_items);
-            dap_string_append_printf(l_str_out, "Count OUT_EXIT: %i \n", l_list_out_ext_items_len);
-            dap_list_free(l_list_out_ext_items);
-            //find RECEIPT
-            dap_list_t *l_list_receipt_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_RECEIPT, NULL);
-            unsigned int l_list_receipt_items_len = dap_list_length(l_list_receipt_items);
-            dap_string_append_printf(l_str_out, "Count RECEIPT: %i \n", l_list_receipt_items_len);
-            dap_list_free(l_list_receipt_items);
-            //find TOKEN_EXT
-            dap_list_t *l_list_token_ext_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_TOKEN_EXT, NULL);
-            unsigned int l_list_token_ext_items_len = dap_list_length(l_list_token_ext_items);
-            dap_string_append_printf(l_str_out, "Count TOKEN_EXT: %i \n", l_list_token_ext_items_len);
-            dap_list_free(l_list_token_ext_items);
-
-            dap_string_append_printf(l_str_out, "\n");
-
-
-
-            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:
diff --git a/modules/net/srv/CMakeLists.txt b/modules/net/srv/CMakeLists.txt
index 25282398a7..b6661c7f1b 100644
--- a/modules/net/srv/CMakeLists.txt
+++ b/modules/net/srv/CMakeLists.txt
@@ -7,12 +7,17 @@ file(GLOB DAP_CHAIN_NET_SRV_HEADERS include/*.h libmaxminddb/*.h)
 
 add_library(${PROJECT_NAME} STATIC ${DAP_CHAIN_NET_SRV_SRCS} ${DAP_CHAIN_NET_SRV_HEADERS})
 
+set(NET_SRV_LIBS dap_chain_net_srv dap_core dap_crypto dap_chain dap_chain_net dap_chain_wallet)
+
 if (CELLFRAME_MODULES MATCHES "modules-dynamic")
-    target_link_libraries(dap_chain_net_srv dap_core dap_crypto dap_chain dap_chain_net dap_chain_wallet dap_modules_dynamic_cdb)
-else()
-    target_link_libraries(dap_chain_net_srv dap_core dap_crypto dap_chain dap_chain_net dap_chain_wallet)
+    set(NET_SRV_LIBS ${NET_SRV_LIBS} dap_modules_dynamic_cdb)
 endif()
 
+#if (CELLFRAME_MODULES MATCHES "srv-stake")
+#    set(NET_SRV_LIBS ${NET_SRV_LIBS} dap_chain_net_srv_stake)
+#endif()
+
+target_link_libraries(${NET_SRV_LIBS})
 target_include_directories(dap_chain_net_srv INTERFACE .)
 target_include_directories(${PROJECT_NAME} PUBLIC include)
 target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../../3rdparty/uthash/src)
diff --git a/modules/net/srv/dap_chain_net_srv_order.c b/modules/net/srv/dap_chain_net_srv_order.c
index b8d0e53fdb..21dcd4d34b 100644
--- a/modules/net/srv/dap_chain_net_srv_order.c
+++ b/modules/net/srv/dap_chain_net_srv_order.c
@@ -30,12 +30,6 @@
 #include "dap_enc_base58.h"
 #include "dap_chain_global_db.h"
 #include "dap_chain_net_srv_countries.h"
-#if DAP_SRV_STAKE_USED
-#include "dap_chain_net_srv_stake.h"
-#else
-static bool dap_chain_net_srv_stake_key_delegated(dap_chain_addr_t *a_addr) { UNUSED(a_addr); return false; }
-#endif
-//#include "dap_chain_net_srv_geoip.h"
 
 #define LOG_TAG "dap_chain_net_srv_order"
 
@@ -544,7 +538,7 @@ static void s_srv_order_callback_notify(void *a_arg, const char a_op_code, const
                 DAP_DELETE(l_gdb_group_str);
                 return;
             }
-            dap_chain_hash_fast_t l_pkey_hash;
+            /*dap_chain_hash_fast_t l_pkey_hash;
             if (!dap_sign_get_pkey_hash(l_sign, &l_pkey_hash)) {
                 dap_chain_global_db_gr_del(dap_strdup(a_key), a_group);
                 DAP_DELETE(l_gdb_group_str);
@@ -556,7 +550,7 @@ static void s_srv_order_callback_notify(void *a_arg, const char a_op_code, const
             uint64_t l_solvency = dap_chain_uint128_to(l_balance);
             if (l_solvency < l_order->price && !dap_chain_net_srv_stake_key_delegated(&l_addr)) {
                 dap_chain_global_db_gr_del(dap_strdup(a_key), a_group);
-            }
+            }*/
         }
         DAP_DELETE(l_gdb_group_str);
     }
diff --git a/modules/service/stake/CMakeLists.txt b/modules/service/stake/CMakeLists.txt
index e50be120ad..e6217cf009 100644
--- a/modules/service/stake/CMakeLists.txt
+++ b/modules/service/stake/CMakeLists.txt
@@ -9,4 +9,4 @@ add_library(${PROJECT_NAME} STATIC ${DAP_SRV_STAKE_SRCS} ${DAP_SRV_STAKE_HEADERS
 
 target_include_directories(dap_chain_crypto INTERFACE .)
 target_include_directories(${PROJECT_NAME} PUBLIC include)
-target_link_libraries(${PROJECT_NAME} dap_core dap_crypto dap_chain dap_chain_crypto dap_chain_net dap_chain_net_srv)
+target_link_libraries(${PROJECT_NAME} dap_core dap_crypto dap_chain dap_chain_crypto dap_chain_net dap_chain_net_srv dap_chain_cs_dag_poa dap_chain_cs_block_poa)
diff --git a/modules/service/stake/dap_chain_net_srv_stake.c b/modules/service/stake/dap_chain_net_srv_stake.c
index 31a05d446c..ea3594eff8 100644
--- a/modules/service/stake/dap_chain_net_srv_stake.c
+++ b/modules/service/stake/dap_chain_net_srv_stake.c
@@ -29,13 +29,15 @@
 #include "dap_chain_common.h"
 #include "dap_chain_mempool.h"
 #include "dap_chain_net_srv_common.h"
+#include "dap_chain_cs_block_poa.h"
+#include "dap_chain_cs_dag_poa.h"
 #include "dap_chain_net_srv_stake.h"
 
 #define LOG_TAG "dap_chain_net_srv_stake"
 
 static int s_cli_srv_stake(int a_argc, char **a_argv, char **a_str_reply);
 
-static dap_chain_net_srv_stake_t *s_srv_stake;
+static dap_chain_net_srv_stake_t *s_srv_stake = NULL;
 
 /**
  * @brief dap_stream_ch_vpn_init Init actions for VPN stream channel
@@ -46,22 +48,27 @@ static dap_chain_net_srv_stake_t *s_srv_stake;
 int dap_chain_net_srv_stake_init()
 {
     dap_chain_node_cli_cmd_item_create("srv_stake", s_cli_srv_stake, "Delegated stake service commands",
-    "srv_stake order create -net <net name> -from_addr <addr> -token <ticker> -coins <value> -cert <name> -fee_percent <value>\n"
-        "\tCreate a new order with specified amount of datoshi to delegate it to the specified address."
-        "The fee with specified percent with this delagation will be returned to the fee address pointed by delegator\n"
-    "srv_stake order remove -net <net name> -order <order hash> [-H hex|base58(default)]\n"
+    "srv_stake order create -net <net name> -addr_hldr <addr> -token <ticker> -coins <value> -cert <name> -fee_percent <value>\n"
+        "\tCreate a new order with specified amount of datoshi to delegate specified cert from the specified address.\n"
+        "\tThe fee with specified percent with this delagation will be returned to the fee address pointed by delegator\n"
+    "srv_stake order declare -net <net name> -wallet <name> -token <ticker> -coins <value> -fee_percent <value>"
+        "\tCreate a new order with specified amount of datoshi and fee which holder is ready to stake.\n"
+    "srv_stake order remove -net <net name> -order <order hash> [-H <hex | base58(default)>]\n"
          "\tRemove order with specified hash\n"
-    "srv_stake order update -net <net name> -order <order hash> -cert <name> [-H hex|base58(default)]"
-                            "{-from_addr <addr> | -token <ticker> -coins <value> | -fee_percent <value>}\n"
+    "srv_stake order update -net <net name> -order <order hash> {-cert <name> | -wallet <name>} [-H <hex | base58(default)>]"
+                            "{[-addr_hldr <addr>] [-token <ticker>] [-coins <value>] [-fee_percent <value>] |"
+                            " | [-token <ticker>] [-coins <value>] -fee_percent <value>]\n"
          "\tUpdate order with specified hash\n"
     "srv_stake order list -net <net name>\n"
          "\tGet the stake orders list within specified net name\n"
-    "srv_stake delegate -order <order hash> -net <net name> -wallet <wallet_name> -fee_addr <addr>\n"
+    "srv_stake delegate -order <order hash> -net <net name> -wallet <name> -fee_addr <addr>\n"
          "\tDelegate tokens with specified order within specified net name. Specify fee address\n"
+    "srv_stake approve -net <net name> -tx <transaction hash> -cert <root cert name>\n"
+         "\tApprove stake transaction by root node certificate within specified net name.\n"
     "srv_stake transactions -net <net name> {-addr <addr from>}\n"
-         "\tShow the list of active stake transactions (optional delegated from addr)\n"
+         "\tShow the list of requested, active and canceled stake transactions (optional delegated from addr)\n"
     "srv_stake invalidate -net <net name> -tx <transaction hash> -wallet <wallet name>\n"
-         "\tInvalidate stake transaction by hash within net name and return stake to specified wallet\n"
+         "\tInvalidate requested stake transaction by hash within net name and return stake to specified wallet\n"
     );
     s_srv_stake = DAP_NEW_Z(dap_chain_net_srv_stake_t);
     uint16_t l_net_count;
@@ -73,6 +80,16 @@ int dap_chain_net_srv_stake_init()
         dap_chain_tx_out_cond_t *l_out_cond;
         int l_out_cond_idx;
         char l_token[DAP_CHAIN_TICKER_SIZE_MAX];
+        size_t l_auth_certs_count = 0;
+        dap_cert_t **l_auth_certs = NULL;
+        for (dap_chain_t *l_chain = l_net_list[i]->pub.chains; l_chain; l_chain = l_chain->next) {
+            l_auth_certs = dap_chain_cs_dag_poa_get_auth_certs(l_chain, &l_auth_certs_count);
+            if (l_auth_certs)
+                break;
+            l_auth_certs = dap_chain_cs_block_poa_get_auth_certs(l_chain, &l_auth_certs_count);
+            if (l_auth_certs)
+                break;
+        }
         // Find all stake transactions
         do {
             l_tx_tmp = dap_chain_ledger_tx_cache_find_out_cond(l_ledger, &l_tx_cur_hash, &l_out_cond, &l_out_cond_idx, l_token);
@@ -83,24 +100,36 @@ int dap_chain_net_srv_stake_init()
                 continue;
             if (dap_chain_ledger_tx_hash_is_used_out_item(l_ledger, &l_tx_cur_hash, l_out_cond_idx))
                 continue;
+            dap_chain_tx_sig_t *l_tx_sig = (dap_chain_tx_sig_t *)dap_chain_datum_tx_item_get(l_tx_tmp, NULL,
+                                                                                             TX_ITEM_TYPE_SIG, NULL);
+            dap_sign_t *l_sign = dap_chain_datum_tx_item_sign_get_sig((dap_chain_tx_sig_t *)l_tx_sig);
+            if (!l_sign) {
+                continue;
+            }
             // Create the stake item
-            dap_chain_net_srv_stake_item_t *l_stake = DAP_NEW_Z(dap_chain_net_srv_stake_item_t);
+            dap_chain_net_srv_stake_item_t *l_stake;
+            bool l_is_new = false;
+            HASH_FIND(hh, s_srv_stake->itemlist, &l_out_cond->params, sizeof(dap_chain_addr_t), l_stake);
+            if (!l_stake) {
+                l_stake = DAP_NEW_Z(dap_chain_net_srv_stake_item_t);
+                l_is_new = true;
+            }
             l_stake->net = l_net_list[i];
             dap_stpcpy(l_stake->token, l_token);
             l_stake->value = l_out_cond->header.value;
-            dap_chain_tx_sig_t *l_tx_sig = (dap_chain_tx_sig_t *)dap_chain_datum_tx_item_get(l_tx_tmp, NULL,
-                    TX_ITEM_TYPE_SIG, NULL);
-            dap_sign_t *l_sign = dap_chain_datum_tx_item_sign_get_sig((dap_chain_tx_sig_t *)l_tx_sig);
-            dap_chain_hash_fast_t l_pkey_hash;
-            if (!dap_sign_get_pkey_hash(l_sign, &l_pkey_hash)) {
-                continue;
+            for (size_t i = 0; i < l_auth_certs_count; i++) {
+                if (!dap_cert_compare_with_sign(l_auth_certs[i], l_sign)) {
+                    l_stake->is_active = true;
+                    break;
+                }
             }
-            dap_chain_addr_fill(&l_stake->addr_from, l_sign->header.type, &l_pkey_hash, l_net_list[i]->pub.id);
-            memcpy(&l_stake->addr_to, l_out_cond->params, sizeof(dap_chain_addr_t));
+            memcpy(&l_stake->signing_addr, &l_out_cond->params, sizeof(dap_chain_addr_t));
+            memcpy(&l_stake->addr_hldr, &l_out_cond->subtype.srv_stake.hldr_addr, sizeof(dap_chain_addr_t));
             memcpy(&l_stake->addr_fee, &l_out_cond->subtype.srv_stake.fee_addr, sizeof(dap_chain_addr_t));
             l_stake->fee_value = l_out_cond->subtype.srv_stake.fee_value;
             memcpy(&l_stake->tx_hash, &l_tx_cur_hash, sizeof(dap_chain_hash_fast_t));
-            HASH_ADD(hh, s_srv_stake->itemlist, tx_hash, sizeof(dap_chain_hash_fast_t), l_stake);
+            if (l_is_new)
+                HASH_ADD(hh, s_srv_stake->itemlist, signing_addr, sizeof(dap_chain_addr_t), l_stake);
         } while (l_tx_tmp);
     }
     DAP_DELETE(l_net_list);
@@ -115,20 +144,106 @@ void dap_chain_net_srv_stake_deinit()
         HASH_DEL(s_srv_stake->itemlist, l_stake);
         DAP_DELETE(l_stake);
     }
-    DAP_DELETE(s_srv_stake);
+    DAP_DEL_Z(s_srv_stake);
+}
+
+static void s_stake_update(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx, bool a_authorized)
+{
+    dap_chain_net_srv_stake_item_t *l_stake;
+    if (a_cond)
+        HASH_FIND(hh, s_srv_stake->itemlist, &a_cond->params, sizeof(dap_chain_addr_t), l_stake);
+    else
+        l_stake = DAP_NEW_Z(dap_chain_net_srv_stake_item_t);
+    assert(l_stake);
+    dap_chain_tx_out_cond_t *l_out_cond = (dap_chain_tx_out_cond_t *)dap_chain_datum_tx_item_get(a_tx, NULL, TX_ITEM_TYPE_OUT_COND, NULL);
+    if (!l_out_cond || l_out_cond->header.subtype != DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE) {
+        // Stake tx is used out
+        HASH_DEL(s_srv_stake->itemlist, l_stake);
+        DAP_DELETE(l_stake);
+        return;
+    }
+    // Update stake parameters
+    if (!a_cond) {
+        // New stake transaction
+        memcpy(&l_stake->signing_addr, &l_out_cond->params, sizeof(dap_chain_addr_t));
+        HASH_ADD(hh, s_srv_stake->itemlist, signing_addr, sizeof(dap_chain_addr_t), l_stake);
+    } else if (memcmp(&a_cond->params, &l_out_cond->params, sizeof(dap_chain_addr_t))) {
+        HASH_DEL(s_srv_stake->itemlist, l_stake);
+        dap_chain_net_srv_stake_item_t *l_stake_cur = NULL;
+        HASH_FIND(hh, s_srv_stake->itemlist, &l_out_cond->params, sizeof(dap_chain_addr_t), l_stake_cur);
+        if (l_stake_cur) {
+            DAP_DELETE(l_stake);
+            l_stake = l_stake_cur;
+        }
+        memcpy(&l_stake->signing_addr, &l_out_cond->params, sizeof(dap_chain_addr_t));
+        if (l_stake_cur)
+            HASH_ADD(hh, s_srv_stake->itemlist, signing_addr, sizeof(dap_chain_addr_t), l_stake);
+    }
+    if (a_authorized)
+        l_stake->is_active = true;
+    memcpy(&l_stake->addr_hldr, &l_out_cond->subtype.srv_stake.hldr_addr, sizeof(dap_chain_addr_t));
+    memcpy(&l_stake->addr_fee, &l_out_cond->subtype.srv_stake.fee_addr, sizeof(dap_chain_addr_t));
+    l_stake->fee_value = l_out_cond->subtype.srv_stake.fee_value;
+    dap_hash_fast(a_tx, dap_chain_datum_tx_get_size(a_tx), &l_stake->tx_hash);
 }
 
-bool dap_chain_net_srv_stake_verificator(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx)
+static bool s_stake_conditions_calc(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx, bool a_owner, bool a_update)
 {
-    UNUSED(a_cond);
-    UNUSED(a_tx);
+    dap_chain_tx_out_cond_t *l_out_cond = NULL;
+    if (!a_cond) {
+        // New stake tx
+        l_out_cond = (dap_chain_tx_out_cond_t *)dap_chain_datum_tx_item_get(a_tx, NULL, TX_ITEM_TYPE_OUT_COND, NULL);
+    } else
+        l_out_cond = a_cond;
+    dap_chain_net_id_t l_cur_net_id = l_out_cond->subtype.srv_stake.hldr_addr.net_id;
+    dap_chain_net_t *l_net = dap_chain_net_by_id(l_cur_net_id);
+    if (!l_net)
+        return false;
+    size_t l_auth_certs_count = 0;
+    dap_cert_t **l_auth_certs = NULL;
+    for (dap_chain_t *l_chain = l_net->pub.chains; l_chain; l_chain = l_chain->next) {
+        l_auth_certs = dap_chain_cs_dag_poa_get_auth_certs(l_chain, &l_auth_certs_count);
+        if (l_auth_certs)
+            break;
+        l_auth_certs = dap_chain_cs_block_poa_get_auth_certs(l_chain, &l_auth_certs_count);
+        if (l_auth_certs)
+            break;
+    }
+    if (!l_auth_certs || !l_auth_certs_count)   // Can't validate stake tx authority for this net
+        return false;
+    dap_chain_tx_sig_t *l_tx_sig = (dap_chain_tx_sig_t *)dap_chain_datum_tx_item_get(a_tx, NULL, TX_ITEM_TYPE_SIG, NULL);
+    dap_sign_t *l_sign = dap_chain_datum_tx_item_sign_get_sig((dap_chain_tx_sig_t *)l_tx_sig);
+    if (!l_sign)
+        return false;
+    for (size_t i = 0; i < l_auth_certs_count; i++) {
+        if (!dap_cert_compare_with_sign(l_auth_certs[i], l_sign)) {
+            if (a_update)
+                s_stake_update(a_cond, a_tx, true);
+            return true;
+        }
+    }
+    if (a_owner) {
+        if (a_update)
+            s_stake_update(a_cond, a_tx, false);
+        return true;
+    }
     return false;
 }
 
+bool dap_chain_net_srv_stake_verificator(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx, bool a_owner)
+{
+    return s_stake_conditions_calc(a_cond, a_tx, a_owner, false);
+}
+
+bool dap_chain_net_srv_stake_updater(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx, bool a_owner)
+{
+    return s_stake_conditions_calc(a_cond, a_tx, a_owner, true);
+}
+
 bool dap_chain_net_srv_stake_key_delegated(dap_chain_addr_t *a_addr)
 {
     if (!s_srv_stake) {
-        return true;
+        return false;
     }
     while (!s_srv_stake->initialized);
 
@@ -137,35 +252,38 @@ bool dap_chain_net_srv_stake_key_delegated(dap_chain_addr_t *a_addr)
     }
     dap_chain_net_srv_stake_item_t *l_stake = NULL;
     HASH_FIND(hh, s_srv_stake->itemlist, a_addr, sizeof(dap_chain_addr_t), l_stake);
-    if (l_stake) { // public key delegated for this network
+    if (l_stake && l_stake->is_active) { // public key delegated for this network
         return true;
     }
     return false;
 }
 
-bool dap_chain_net_srv_stake_validator(dap_chain_addr_t *a_addr, dap_chain_datum_tx_t *a_tx)
+bool dap_chain_net_srv_stake_validator(dap_chain_addr_t *a_addr, dap_chain_datum_t *a_datum)
 {
-    if (!s_srv_stake) {
-        return true;
+    if (!s_srv_stake) { // Drop all atoms if stake service inactivated
+        return false;
     }
     while (!s_srv_stake->initialized);
 
-    if (!a_addr || !a_tx) {
+    if (!a_addr || !a_datum) {
         return false;
     }
     dap_chain_net_srv_stake_item_t *l_stake = NULL;
     HASH_FIND(hh, s_srv_stake->itemlist, a_addr, sizeof(dap_chain_addr_t), l_stake);
-    if (l_stake == NULL) { // public key not delegated for this network
-        return true;
+    if (!l_stake || !l_stake->is_active) { // public key not delegated for this network
+        return false;
     }
-    dap_chain_tx_sig_t *l_tx_sig = (dap_chain_tx_sig_t *)dap_chain_datum_tx_item_get(a_tx, NULL, TX_ITEM_TYPE_SIG, NULL);
+    if (a_datum->header.type_id != DAP_CHAIN_DATUM_TX)
+        return true;
+    dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t *)a_datum->data;
+    dap_chain_tx_sig_t *l_tx_sig = (dap_chain_tx_sig_t *)dap_chain_datum_tx_item_get(l_tx, NULL, TX_ITEM_TYPE_SIG, NULL);
     dap_sign_t *l_sign = dap_chain_datum_tx_item_sign_get_sig((dap_chain_tx_sig_t *)l_tx_sig);
     dap_chain_hash_fast_t l_pkey_hash = {};
     dap_sign_get_pkey_hash(l_sign, &l_pkey_hash);
     dap_chain_addr_t l_owner_addr = {};
     dap_chain_addr_fill(&l_owner_addr, l_sign->header.type, &l_pkey_hash, a_addr->net_id);
     uint64_t l_outs_sum = 0, l_fee_sum = 0;
-    dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(a_tx, TX_ITEM_TYPE_OUT_ALL, NULL);
+    dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT_ALL, NULL);
     uint32_t l_out_idx_tmp = 0; // current index of 'out' item
     for (dap_list_t *l_list_tmp = l_list_out_items; l_list_tmp; l_list_tmp = dap_list_next(l_list_tmp), l_out_idx_tmp++) {
         dap_chain_tx_item_type_t l_type = *(uint8_t *)l_list_tmp->data;
@@ -195,7 +313,7 @@ bool dap_chain_net_srv_stake_validator(dap_chain_addr_t *a_addr, dap_chain_datum
 
 static dap_chain_datum_tx_t *s_stake_tx_create(dap_chain_net_srv_stake_item_t *a_stake, dap_chain_wallet_t *a_wallet)
 {
-    if (!a_stake || !a_stake->net || !a_stake->addr_to.addr_ver || !a_stake->addr_from.addr_ver ||
+    if (!a_stake || !a_stake->net || !a_stake->signing_addr.addr_ver || !a_stake->addr_hldr.addr_ver ||
             !a_stake->addr_fee.addr_ver || !*a_stake->token || !a_wallet) {
         return NULL;
     }
@@ -205,7 +323,7 @@ static dap_chain_datum_tx_t *s_stake_tx_create(dap_chain_net_srv_stake_item_t *a
 
     dap_ledger_t *l_ledger = dap_chain_ledger_by_net_name(a_stake->net->pub.name);
     dap_chain_addr_t *l_owner_addr = (dap_chain_addr_t *)dap_chain_wallet_get_addr(a_wallet, a_stake->net->pub.id);
-    if (memcmp(l_owner_addr, &a_stake->addr_from, sizeof(dap_chain_addr_t))) {
+    if (memcmp(l_owner_addr, &a_stake->addr_hldr, sizeof(dap_chain_addr_t))) {
         log_it(L_WARNING, "Odrer and wallet address do not match");
         return NULL;
     }
@@ -233,8 +351,9 @@ static dap_chain_datum_tx_t *s_stake_tx_create(dap_chain_net_srv_stake_item_t *a
     // add 'out_cond' & 'out' items
     {
         dap_chain_net_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_STAKE_ID };
-        dap_chain_tx_out_cond_t *l_tx_out = dap_chain_datum_tx_item_out_cond_create_srv_stake(l_uid, a_stake->value, a_stake->fee_value, &a_stake->addr_fee,
-                                                                                              (void *)&a_stake->addr_to, sizeof(dap_chain_addr_t));
+        dap_chain_tx_out_cond_t *l_tx_out = dap_chain_datum_tx_item_out_cond_create_srv_stake(l_uid, a_stake->value, a_stake->fee_value,
+                                                                                              &a_stake->addr_fee, &a_stake->addr_hldr,
+                                                                                              (void *)&a_stake->signing_addr, sizeof(dap_chain_addr_t));
         if (!l_tx_out) {
             dap_chain_datum_tx_delete(l_tx);
             DAP_DELETE(l_owner_addr);
@@ -285,6 +404,59 @@ static bool s_stake_tx_put(dap_chain_datum_tx_t *a_tx, dap_chain_net_t *a_net)
     return true;
 }
 
+static dap_chain_datum_tx_t *s_stake_tx_approve(dap_chain_net_srv_stake_item_t *a_stake, dap_cert_t *a_cert)
+{
+    // create empty transaction
+    dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create();
+
+    dap_ledger_t *l_ledger = dap_chain_ledger_by_net_name(a_stake->net->pub.name);
+
+    // create and add reciept
+    dap_chain_net_srv_price_unit_uid_t l_unit = { .uint32 = SERV_UNIT_UNDEFINED};
+    dap_chain_net_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_STAKE_ID };
+    dap_chain_datum_tx_receipt_t *l_receipt = dap_chain_datum_tx_receipt_create(l_uid, l_unit, 0, a_stake->value, NULL, 0);
+    dap_chain_datum_tx_add_item(&l_tx, (byte_t *)l_receipt);
+    DAP_DELETE(l_receipt);
+
+    // add 'in' item to buy from conditional transaction
+    dap_chain_datum_tx_t *l_cond_tx = dap_chain_ledger_tx_find_by_hash(l_ledger, &a_stake->tx_hash);
+    if (!l_cond_tx) {
+        log_it(L_WARNING, "Requested conditional transaction not found");
+        return NULL;
+    }
+    int l_prev_cond_idx;
+    dap_chain_tx_out_cond_t *l_tx_out_cond = dap_chain_datum_tx_out_cond_get(l_cond_tx, &l_prev_cond_idx);
+    if (dap_chain_ledger_tx_hash_is_used_out_item(l_ledger, &a_stake->tx_hash, l_prev_cond_idx)) {
+        log_it(L_WARNING, "Requested conditional transaction is already used out");
+        return NULL;
+    }
+    assert(l_tx_out_cond->header.value == a_stake->value);
+    dap_chain_datum_tx_add_in_cond_item(&l_tx, &a_stake->tx_hash, l_prev_cond_idx, 0);
+
+    // add 'out_cond' item
+    {
+        dap_chain_net_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_STAKE_ID };
+        dap_chain_tx_out_cond_t *l_tx_out = dap_chain_datum_tx_item_out_cond_create_srv_stake(l_uid, a_stake->value, a_stake->fee_value,
+                                                                                              &a_stake->addr_fee, &a_stake->addr_hldr,
+                                                                                              (void *)&a_stake->signing_addr, sizeof(dap_chain_addr_t));
+        if (!l_tx_out) {
+            dap_chain_datum_tx_delete(l_tx);
+            log_it(L_ERROR, "Can't compose the transaction conditional output");
+            return NULL;
+        }
+        dap_chain_datum_tx_add_item(&l_tx, (const uint8_t *)l_tx_out);
+        DAP_DELETE(l_tx_out);
+    }
+
+    // add 'sign' items
+    if(dap_chain_datum_tx_add_sign_item(&l_tx, a_cert->enc_key) != 1) {
+        dap_chain_datum_tx_delete(l_tx);
+        log_it( L_ERROR, "Can't add sign output");
+        return NULL;
+    }
+    return l_tx;
+}
+
 static bool s_stake_tx_invalidate(dap_chain_net_srv_stake_item_t *a_stake, dap_chain_wallet_t *a_wallet)
 {
     // create empty transaction
@@ -339,15 +511,20 @@ static bool s_stake_tx_invalidate(dap_chain_net_srv_stake_item_t *a_stake, dap_c
 char *s_stake_order_create(dap_chain_net_srv_stake_item_t *a_item, dap_enc_key_t *l_key)
 {
     dap_chain_hash_fast_t l_tx_hash = {};
-    dap_srv_stake_order_ext_t l_ext;
-    memcpy(&l_ext.addr_from, &a_item->addr_from, sizeof(dap_chain_addr_t));
-    memcpy(&l_ext.addr_to, &a_item->addr_to, sizeof(dap_chain_addr_t));
+    dap_srv_stake_order_ext_t l_ext = {};
+    memcpy(&l_ext.addr_hldr, &a_item->addr_hldr, sizeof(dap_chain_addr_t));
+    dap_chain_net_srv_order_direction_t l_dir = SERV_DIR_SELL;
+    if (memcmp(&a_item->signing_addr, &l_ext.signing_addr, sizeof(dap_chain_addr_t))) {
+        // Signing addr is not empty
+        l_dir = SERV_DIR_BUY;
+        memcpy(&l_ext.signing_addr, &a_item->signing_addr, sizeof(dap_chain_addr_t));
+    }
     l_ext.fee_value = a_item->fee_value;
     uint32_t l_ext_size = sizeof(dap_srv_stake_order_ext_t);
     dap_chain_node_addr_t *l_node_addr = dap_chain_net_get_cur_addr(a_item->net);
     dap_chain_net_srv_price_unit_uid_t l_unit = { .uint32 =  SERV_UNIT_UNDEFINED};
     dap_chain_net_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_STAKE_ID };
-    char *l_order_hash_str = dap_chain_net_srv_order_create(a_item->net, SERV_DIR_BUY, l_uid, *l_node_addr,
+    char *l_order_hash_str = dap_chain_net_srv_order_create(a_item->net, l_dir, l_uid, *l_node_addr,
                                                             l_tx_hash, a_item->value, l_unit, a_item->token, 0,
                                                             (uint8_t *)&l_ext, l_ext_size, NULL, 0, l_key);
     return l_order_hash_str;
@@ -370,12 +547,12 @@ dap_chain_net_srv_stake_item_t *s_stake_item_from_order(dap_chain_net_t *a_net,
     dap_chain_addr_t l_cert_addr;
     dap_chain_addr_fill(&l_cert_addr, l_sign->header.type, &l_pkey_hash, a_net->pub.id);
     dap_chain_net_srv_stake_item_t *l_item = DAP_NEW_Z(dap_chain_net_srv_stake_item_t);
-    if (memcmp(&l_cert_addr, &l_ext->addr_to, sizeof(dap_chain_addr_t))) {
-        log_it(L_WARNING, "Order sign addr & addr_to are different");
+    if (memcmp(&l_cert_addr, &l_ext->signing_addr, sizeof(dap_chain_addr_t))) {
+        log_it(L_WARNING, "Order sign addr & signing_addr are different");
         return NULL;
     }
-    memcpy(&l_item->addr_from, &l_ext->addr_from, sizeof(dap_chain_addr_t));
-    memcpy(&l_item->addr_to, &l_ext->addr_to, sizeof(dap_chain_addr_t));
+    memcpy(&l_item->addr_hldr, &l_ext->addr_hldr, sizeof(dap_chain_addr_t));
+    memcpy(&l_item->signing_addr, &l_ext->signing_addr, sizeof(dap_chain_addr_t));
     l_item->fee_value = l_ext->fee_value;
     l_item->net = a_net;
     l_item->value = a_order->price;
@@ -386,7 +563,7 @@ dap_chain_net_srv_stake_item_t *s_stake_item_from_order(dap_chain_net_t *a_net,
 static int s_cli_srv_stake_order(int a_argc, char **a_argv, int a_arg_index, char **a_str_reply, const char *a_hash_out_type)
 {
     enum {
-        CMD_NONE, CMD_CREATE, CMD_REMOVE, CMD_LIST, CMD_UPDATE
+        CMD_NONE, CMD_CREATE, CMD_DECLARE, CMD_REMOVE, CMD_LIST, CMD_UPDATE
     };
     int l_cmd_num = CMD_NONE;
     if(dap_chain_node_cli_find_option_val(a_argv, a_arg_index, min(a_argc, a_arg_index + 1), "create", NULL)) {
@@ -405,7 +582,7 @@ static int s_cli_srv_stake_order(int a_argc, char **a_argv, int a_arg_index, cha
     switch (l_cmd_num) {
         case CMD_CREATE: {
             const char *l_net_str = NULL, *l_token_str = NULL, *l_coins_str = NULL;
-            const char *l_addr_from_str = NULL, *l_cert_str = NULL, *l_fee_str = NULL;
+            const char *l_addr_hldr_str = NULL, *l_cert_str = NULL, *l_fee_str = NULL;
             dap_chain_net_t *l_net = NULL;
             dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-net", &l_net_str);
             if (!l_net_str) {
@@ -431,18 +608,18 @@ static int s_cli_srv_stake_order(int a_argc, char **a_argv, int a_arg_index, cha
                 dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order create' required parameter -coins");
                 return -7;
             }
-            uint64_t l_value = strtoull(l_coins_str, NULL, 10);
+            uint64_t l_value = strtoull(l_coins_str, NULL, 10); // TODO add possibility to work with 256-bit format
             if (!l_value) {
                 dap_chain_node_cli_set_reply_text(a_str_reply, "Format -coins <unsigned long long>");
                 return -8;
             }
-            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-addr_from", &l_addr_from_str);
-            if (!l_addr_from_str) {
-                dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order create' required parameter -addr_from");
+            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-addr_hldr", &l_addr_hldr_str);
+            if (!l_addr_hldr_str) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order create' required parameter -addr_hldr");
                 return -9;
             }
-            dap_chain_addr_t *l_addr_from = dap_chain_addr_from_str(l_addr_from_str);
-            if (!l_addr_from) {
+            dap_chain_addr_t *l_addr_hldr = dap_chain_addr_from_str(l_addr_hldr_str);
+            if (!l_addr_hldr) {
                 dap_chain_node_cli_set_reply_text(a_str_reply, "Wrong address format");
                 return -10;
             }
@@ -463,7 +640,7 @@ static int s_cli_srv_stake_order(int a_argc, char **a_argv, int a_arg_index, cha
             }
             long double l_fee = strtold(l_fee_str, NULL);
             if (!l_fee) {
-                dap_chain_node_cli_set_reply_text(a_str_reply, "Format -fee_percent <long double> %");
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Format -fee_percent <long double>(%)");
                 return -12;
             }
             // Create the stake item
@@ -471,11 +648,11 @@ static int s_cli_srv_stake_order(int a_argc, char **a_argv, int a_arg_index, cha
             l_stake->net = l_net;
             dap_stpcpy(l_stake->token, l_token_str);
             l_stake->value = l_value;
-            memcpy(&l_stake->addr_from, l_addr_from, sizeof(dap_chain_addr_t));
-            dap_chain_addr_t *l_addr_to = dap_cert_to_addr(l_cert, l_net->pub.id);
-            memcpy(&l_stake->addr_to, l_addr_to, sizeof(dap_chain_addr_t));
-            DAP_DELETE(l_addr_from);
-            DAP_DELETE(l_addr_to);
+            memcpy(&l_stake->addr_hldr, l_addr_hldr, sizeof(dap_chain_addr_t));
+            dap_chain_addr_t *l_signing_addr = dap_cert_to_addr(l_cert, l_net->pub.id);
+            memcpy(&l_stake->signing_addr, l_signing_addr, sizeof(dap_chain_addr_t));
+            DAP_DELETE(l_addr_hldr);
+            DAP_DELETE(l_signing_addr);
             l_stake->fee_value = l_fee;
             // Create the order & put it to GDB
             char *l_order_hash_str = s_stake_order_create(l_stake, l_cert->enc_key);
@@ -489,6 +666,85 @@ static int s_cli_srv_stake_order(int a_argc, char **a_argv, int a_arg_index, cha
                 return -15;
             }
         } break;
+        case CMD_DECLARE: {
+            const char *l_net_str = NULL, *l_token_str = NULL, *l_coins_str = NULL;
+            const char *l_wallet_str = NULL, *l_fee_str = NULL;
+            dap_chain_net_t *l_net = NULL;
+            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-net", &l_net_str);
+            if (!l_net_str) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order declare' required parameter -net");
+                return -3;
+            }
+            l_net = dap_chain_net_by_name(l_net_str);
+            if (!l_net) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Network %s not found", l_net_str);
+                return -4;
+            }
+            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-token", &l_token_str);
+            if (!l_token_str) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order declare' required parameter -token");
+                return -5;
+            }
+            if (!dap_chain_ledger_token_ticker_check(l_net->pub.ledger, l_token_str)) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Token ticker %s not found", l_token_str);
+                return -6;
+            }
+            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-coins", &l_coins_str);
+            if (!l_coins_str) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order declare' required parameter -coins");
+                return -7;
+            }
+            uint64_t l_value = strtoull(l_coins_str, NULL, 10); // TODO add possibility to work with 256-bit format
+            if (!l_value) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Format -coins <unsigned long long>");
+                return -8;
+            }
+            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-wallet", &l_wallet_str);
+            if (!l_wallet_str) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order declare' required parameter -wallet");
+                return -9;
+            }
+            dap_chain_wallet_t *l_wallet = dap_chain_wallet_open(l_wallet_str, dap_chain_wallet_get_path(g_config));
+            if (!l_wallet) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Specified wallet not found");
+                return -18;
+            }
+            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-fee_percent", &l_fee_str);
+            if (!l_fee_str) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order declare' required parameter -fee_percent");
+                return -11;
+            }
+            long double l_fee = strtold(l_fee_str, NULL);
+            if (!l_fee) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Format -fee_percent <long double>(%)");
+                return -12;
+            }
+            uint64_t l_balance = dap_chain_uint128_to(dap_chain_wallet_get_balance(l_wallet, l_net->pub.id, l_token_str));
+            if (l_balance < l_value) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Insufficient coins for token %s in wallet '%s'", l_token_str, l_wallet_str);
+                return -13;
+            }
+            // Create the stake item
+            dap_chain_net_srv_stake_item_t *l_stake = DAP_NEW_Z(dap_chain_net_srv_stake_item_t);
+            l_stake->net = l_net;
+            dap_stpcpy(l_stake->token, l_token_str);
+            l_stake->value = l_value;
+            dap_chain_addr_t *l_addr_hldr = dap_chain_wallet_get_addr(l_wallet, l_net->pub.id);
+            memcpy(&l_stake->addr_hldr, l_addr_hldr, sizeof(dap_chain_addr_t));
+            DAP_DELETE(l_addr_hldr);
+            l_stake->fee_value = l_fee;
+            // Create the order & put it to GDB
+            char *l_order_hash_str = s_stake_order_create(l_stake, dap_chain_wallet_get_key(l_wallet, 0));
+            if (l_order_hash_str) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Successfully created order %s", l_order_hash_str);
+                DAP_DELETE(l_order_hash_str);
+                DAP_DELETE(l_stake);
+            } else {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Can't compose the order");
+                DAP_DELETE(l_stake);
+                return -15;
+            }
+        } break;
         case CMD_REMOVE: {
             const char *l_net_str = NULL, *l_order_hash_str = NULL;
             dap_chain_net_t *l_net = NULL;
@@ -531,9 +787,10 @@ static int s_cli_srv_stake_order(int a_argc, char **a_argv, int a_arg_index, cha
         } break;
         case CMD_UPDATE: {
             const char *l_net_str = NULL, *l_token_str = NULL, *l_coins_str = NULL;
-            const char *l_addr_from_str = NULL, *l_cert_str = NULL, *l_fee_str = NULL;
-            char *l_order_hash_str = NULL;
+            const char *l_addr_hldr_str = NULL, *l_cert_str = NULL, *l_fee_str = NULL;
+            const char *l_order_hash_str = NULL, *l_wallet_str = NULL;
             dap_chain_net_t *l_net = NULL;
+            dap_enc_key_t *l_key = NULL;
             dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-net", &l_net_str);
             if (!l_net_str) {
                 dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order update' required parameter -net");
@@ -569,16 +826,6 @@ static int s_cli_srv_stake_order(int a_argc, char **a_argv, int a_arg_index, cha
                     dap_chain_node_cli_set_reply_text(a_str_reply, "Can't find order %s\n", l_order_hash_base58_str);
                 return -14;
             }
-            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-wallet", &l_cert_str);
-            if (!l_cert_str) {
-                dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order update' required parameter -cert");
-                return -9;
-            }
-            dap_cert_t *l_cert = dap_cert_find_by_name(l_cert_str);
-            if (!l_cert) {
-                dap_chain_node_cli_set_reply_text(a_str_reply, "Can't load cert %s", l_cert_str);
-                return -10;
-            }
             dap_chain_net_srv_stake_item_t *l_stake = s_stake_item_from_order(l_net, l_order);
             dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-token", &l_token_str);
             if (l_token_str) {
@@ -591,7 +838,7 @@ static int s_cli_srv_stake_order(int a_argc, char **a_argv, int a_arg_index, cha
             }
             dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-coins", &l_coins_str);
             if (l_coins_str) {
-                uint64_t l_value = strtoull(l_coins_str, NULL, 10);
+                uint64_t l_value = strtoull(l_coins_str, NULL, 10); // TODO add possibility to work with 256-bit format
                 if (!l_value) {
                     dap_chain_node_cli_set_reply_text(a_str_reply, "Format -coins <unsigned long long>");
                     DAP_DELETE(l_stake);
@@ -608,19 +855,60 @@ static int s_cli_srv_stake_order(int a_argc, char **a_argv, int a_arg_index, cha
                     return -12;
                 }
             }
-            if (!l_token_str && !l_coins_str && !l_addr_from_str && !l_fee_str) {
+            dap_chain_addr_t l_empty_addr = {};
+            if (memcmp(&l_stake->signing_addr, &l_empty_addr, sizeof(dap_chain_addr_t))) {
+                // It's a buying order
+                dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-cert", &l_cert_str);
+                if (!l_cert_str) {
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order update' requires parameter -cert for buying orders");
+                    return -9;
+                }
+                dap_cert_t *l_cert = dap_cert_find_by_name(l_cert_str);
+                if (!l_cert) {
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "Can't load cert %s", l_cert_str);
+                    DAP_DELETE(l_stake);
+                    return -10;
+                }
+                l_key = l_cert->enc_key;
+                dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-addr_hldr", &l_addr_hldr_str);
+                if (l_addr_hldr_str) {
+                    dap_chain_addr_t *l_addr_hldr = dap_chain_addr_from_str(l_addr_hldr_str);
+                    if (!l_addr_hldr) {
+                        dap_chain_node_cli_set_reply_text(a_str_reply, "Wrong address format");
+                        DAP_DELETE(l_stake);
+                        return -14;
+                    }
+                    memcpy(&l_stake->addr_hldr, l_addr_hldr, sizeof(dap_chain_addr_t));
+                    DAP_DELETE(l_addr_hldr);
+                }
+            } else {    // It's a selling order
+                dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-wallet", &l_wallet_str);
+                if (!l_wallet_str) {
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order update' requires parameter -wallet for selling orders");
+                    return -7;
+                }
+                dap_chain_wallet_t *l_wallet = dap_chain_wallet_open(l_wallet_str, dap_chain_wallet_get_path(g_config));
+                if (!l_wallet) {
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "Specified wallet not found");
+                    return -18;
+                }
+                uint64_t l_balance = dap_chain_uint128_to(dap_chain_wallet_get_balance(l_wallet, l_net->pub.id, l_stake->token));
+                if (l_balance < l_stake->value) {
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "Insufficient coins for token %s in wallet '%s'", l_token_str, l_wallet_str);
+                    return -11;
+                }
+                l_key = dap_chain_wallet_get_key(l_wallet, 0);
+            }
+            if (!l_token_str && !l_coins_str && !l_addr_hldr_str && !l_fee_str) {
                 dap_chain_node_cli_set_reply_text(a_str_reply, "At least one of updating parameters is mandatory");
                 DAP_DELETE(l_stake);
                 return -16;
             }
-            dap_chain_addr_t *l_addr_to = dap_cert_to_addr(l_cert, l_net->pub.id);
-            memcpy(&l_stake->addr_to, l_addr_to, sizeof(dap_chain_addr_t));
-            DAP_DELETE(l_addr_to);
             // Create the order & put it to GDB
             dap_chain_net_srv_order_delete_by_hash_str(l_net, l_order_hash_hex_str);
             DAP_DELETE(l_order_hash_hex_str);
             DAP_DELETE(l_order_hash_base58_str);
-            l_order_hash_hex_str = s_stake_order_create(l_stake, l_cert->enc_key);
+            l_order_hash_hex_str = s_stake_order_create(l_stake, l_key);
             if(l_order_hash_hex_str) {
                 if(!dap_strcmp(a_hash_out_type, "hex")) {
                     dap_chain_node_cli_set_reply_text(a_str_reply, "Successfully created order %s", l_order_hash_hex_str);
@@ -662,7 +950,7 @@ static int s_cli_srv_stake_order(int a_argc, char **a_argv, int a_arg_index, cha
                     continue;
                 // TODO add filters to list (token, address, etc.)
                 l_stake = s_stake_item_from_order(l_net, l_order);
-                char *l_addr = dap_chain_addr_to_str(&l_stake->addr_to);
+                char *l_addr = dap_chain_addr_to_str(&l_stake->signing_addr);
                 dap_string_append_printf(l_reply_str, "%s %"DAP_UINT64_FORMAT_U" %s %s %Lf\n", l_orders[i].key, l_stake->value, l_stake->token,
                                          l_addr, l_stake->fee_value);
                 DAP_DELETE(l_addr);
@@ -686,19 +974,18 @@ static int s_cli_srv_stake_order(int a_argc, char **a_argv, int a_arg_index, cha
 static int s_cli_srv_stake(int a_argc, char **a_argv, char **a_str_reply)
 {
     enum {
-        CMD_NONE, CMD_ORDER, CMD_DELEGATE, CMD_TX, CMD_INVALIDATE
+        CMD_NONE, CMD_ORDER, CMD_DELEGATE, CMD_APPROVE, CMD_TX, CMD_INVALIDATE
     };
     int l_arg_index = 1;
 
     const char * l_hash_out_type = NULL;
     dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "-H", &l_hash_out_type);
     if(!l_hash_out_type)
-        l_hash_out_type = "hex";
-    if(dap_strcmp(l_hash_out_type,"hex") && dap_strcmp(l_hash_out_type,"base58")) {
+        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_num = CMD_NONE;
     if (dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "order", NULL)) {
         l_cmd_num = CMD_ORDER;
@@ -706,6 +993,9 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, char **a_str_reply)
     else if (dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "delegate", NULL)) {
         l_cmd_num = CMD_DELEGATE;
     }
+    else if (dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "approve", NULL)) {
+        l_cmd_num = CMD_APPROVE;
+    }
     else if (dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "transactions", NULL)) {
         l_cmd_num = CMD_TX;
     }
@@ -764,15 +1054,16 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, char **a_str_reply)
                 }
                 memcpy(&l_stake->addr_fee, l_addr_fee, sizeof(dap_chain_addr_t));
                 DAP_DELETE(l_addr_fee);
+                dap_chain_addr_t *l_hldr_addr = dap_chain_wallet_get_addr(l_wallet, l_net->pub.id);
+                memcpy(&l_stake->addr_hldr, l_hldr_addr, sizeof(dap_chain_addr_t));
+                DAP_DELETE(l_hldr_addr);
                 // Create conditional transaction
                 dap_chain_datum_tx_t *l_tx = s_stake_tx_create(l_stake, l_wallet);
                 dap_chain_wallet_close(l_wallet);
-                if (l_tx) {
-                    dap_hash_fast(l_tx, dap_chain_datum_tx_get_size(l_tx), &l_stake->tx_hash);
-                    if (s_stake_tx_put(l_tx, l_net)) {
-                        // TODO send request to order owner to delete it
-                        dap_chain_net_srv_order_delete_by_hash_str(l_net, l_order_hash_str);
-                    }
+                if (l_tx && s_stake_tx_put(l_tx, l_net)) {
+                    dap_hash_fast(l_tx, dap_chain_datum_tx_get_size(l_tx), &l_stake->tx_hash);         
+                    // TODO send request to order owner to delete it
+                    dap_chain_net_srv_order_delete_by_hash_str(l_net, l_order_hash_str);
                 }
                 DAP_DELETE(l_order);
                 dap_chain_node_cli_set_reply_text(a_str_reply, l_tx ? "Stake transaction has done" :
@@ -781,13 +1072,65 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, char **a_str_reply)
                     DAP_DELETE(l_stake);
                     return -19;
                 }
-                HASH_ADD(hh, s_srv_stake->itemlist, addr_to, sizeof(dap_chain_addr_t), l_stake);
+                HASH_ADD(hh, s_srv_stake->itemlist, signing_addr, sizeof(dap_chain_addr_t), l_stake);
             } else {
                 DAP_DELETE(l_addr_fee);
                 dap_chain_node_cli_set_reply_text(a_str_reply, "Specified order not found");
                 return -14;
             }
         } break;
+        case CMD_APPROVE: {
+            const char *l_net_str = NULL, *l_tx_hash_str = NULL, *l_cert_str = NULL;
+            l_arg_index++;
+            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-net", &l_net_str);
+            if (!l_net_str) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'approve' required parameter -net");
+                return -3;
+            }
+            dap_chain_net_t *l_net = dap_chain_net_by_name(l_net_str);
+            if (!l_net) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Network %s not found", l_net_str);
+                return -4;
+            }
+            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-cert", &l_cert_str);
+            if (!l_cert_str) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'approve' required parameter -cert");
+                return -17;
+            }
+            dap_cert_t *l_cert = dap_cert_find_by_name(l_cert_str);
+            if (!l_cert) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Specified certificate not found");
+                return -18;
+            }
+            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-tx", &l_tx_hash_str);
+            if (!l_tx_hash_str) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'delegate' required parameter -tx");
+                return -13;
+            }
+            dap_chain_hash_fast_t l_tx_hash = {};
+            int l_result = dap_chain_hash_fast_from_str(l_tx_hash_str, &l_tx_hash);
+            if (l_result)
+                l_result = dap_enc_base58_decode(l_tx_hash_str, &l_tx_hash) - sizeof(dap_chain_hash_fast_t);
+            if (l_result) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Invalid transaction hash format");
+                return -14;
+            }
+            dap_chain_net_srv_stake_item_t *l_stake = NULL, *l_tmp;
+            HASH_ITER(hh, s_srv_stake->itemlist, l_stake, l_tmp) {
+                if (!memcmp(&l_stake->tx_hash, &l_tx_hash, sizeof(dap_chain_hash_fast_t))) {
+                    break;
+                }
+            }
+            if (!l_stake) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Transaction %s not found", l_tx_hash_str);
+                return -20;
+            }
+            dap_chain_datum_tx_t *l_tx = s_stake_tx_approve(l_stake, l_cert);
+            if (l_tx && s_stake_tx_put(l_tx, l_net)) {
+                dap_hash_fast(l_tx, dap_chain_datum_tx_get_size(l_tx), &l_stake->tx_hash);
+                l_stake->is_active = true;
+            }
+        } break;
         case CMD_TX: {
             const char *l_net_str = NULL;
             l_arg_index++;
@@ -808,15 +1151,15 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, char **a_str_reply)
                     continue;
                 }
                 char *l_tx_hash_str = dap_chain_hash_fast_to_str_new(&l_stake->tx_hash);
-                char *l_addr_from_str = dap_chain_addr_to_str(&l_stake->addr_from);
-                char *l_addr_to_str = dap_chain_addr_to_str(&l_stake->addr_to);
+                char *l_addr_hldr_str = dap_chain_addr_to_str(&l_stake->addr_hldr);
+                char *l_signing_addr_str = dap_chain_addr_to_str(&l_stake->signing_addr);
                 char *l_addr_fee_str = dap_chain_addr_to_str(&l_stake->addr_fee);
                 dap_string_append_printf(l_reply_str, "%s %s %"DAP_UINT64_FORMAT_U" %s %s %s %Lf\n", l_tx_hash_str, l_stake->token,
-                                         l_stake->value, l_addr_from_str, l_addr_to_str,
+                                         l_stake->value, l_addr_hldr_str, l_signing_addr_str,
                                          l_addr_fee_str, l_stake->fee_value);
                 DAP_DELETE(l_tx_hash_str);
-                DAP_DELETE(l_addr_from_str);
-                DAP_DELETE(l_addr_to_str);
+                DAP_DELETE(l_addr_hldr_str);
+                DAP_DELETE(l_signing_addr_str);
                 DAP_DELETE(l_addr_fee_str);
             }
             if (!l_reply_str->len) {
diff --git a/modules/service/stake/include/dap_chain_net_srv_stake.h b/modules/service/stake/include/dap_chain_net_srv_stake.h
index 4a89391531..c539cee32a 100644
--- a/modules/service/stake/include/dap_chain_net_srv_stake.h
+++ b/modules/service/stake/include/dap_chain_net_srv_stake.h
@@ -27,15 +27,16 @@
 #include "dap_chain_net_srv.h"
 #include "dap_chain_net_srv_order.h"
 
-#define DAP_CHAIN_NET_SRV_STAKE_ID 0x3
+#define DAP_CHAIN_NET_SRV_STAKE_ID 0x13
 
 typedef struct dap_chain_net_srv_stake_item {
+    bool is_active;
     dap_chain_net_t *net;
     char token[DAP_CHAIN_TICKER_SIZE_MAX];
     uint64_t value;
-    dap_chain_addr_t addr_from;
-    dap_chain_addr_t addr_to;
+    dap_chain_addr_t addr_hldr;
     dap_chain_addr_t addr_fee;
+    dap_chain_addr_t signing_addr;
     long double fee_value;
     dap_chain_hash_fast_t tx_hash;
     dap_chain_hash_fast_t order_hash;
@@ -43,8 +44,8 @@ typedef struct dap_chain_net_srv_stake_item {
 } dap_chain_net_srv_stake_item_t;
 
 typedef struct dap_srv_stake_order_ext {
-    dap_chain_addr_t addr_from;
-    dap_chain_addr_t addr_to;
+    dap_chain_addr_t addr_hldr;
+    dap_chain_addr_t signing_addr;
     long double fee_value;
 } dap_srv_stake_order_ext_t;
 
@@ -55,6 +56,7 @@ typedef struct dap_chain_net_srv_stake {
 
 int dap_chain_net_srv_stake_init();
 void dap_chain_net_srv_stake_deinit();
-bool dap_chain_net_srv_stake_verificator(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx);
-bool dap_chain_net_srv_stake_validator(dap_chain_addr_t *a_addr, dap_chain_datum_tx_t *a_tx);
+bool dap_chain_net_srv_stake_verificator(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx, bool a_owner);
+bool dap_chain_net_srv_stake_updater(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx, bool a_owner);
+bool dap_chain_net_srv_stake_validator(dap_chain_addr_t *a_addr, dap_chain_datum_t *a_datum);
 bool dap_chain_net_srv_stake_key_delegated(dap_chain_addr_t *a_addr);
diff --git a/modules/service/xchange/dap_chain_net_srv_xchange.c b/modules/service/xchange/dap_chain_net_srv_xchange.c
index d36662f727..df904b6701 100644
--- a/modules/service/xchange/dap_chain_net_srv_xchange.c
+++ b/modules/service/xchange/dap_chain_net_srv_xchange.c
@@ -101,8 +101,10 @@ void dap_chain_net_srv_xchange_deinit()
     DAP_DELETE(s_srv_xchange);
 }
 
-bool dap_chain_net_srv_xchange_verificator(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx)
+bool dap_chain_net_srv_xchange_verificator(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx, bool a_owner)
 {
+    if (a_owner)
+        return true;
     /* Check the condition for verification success
      * a_cond.srv_xchange.rate >= a_tx.out.rate
      */
diff --git a/modules/service/xchange/include/dap_chain_net_srv_xchange.h b/modules/service/xchange/include/dap_chain_net_srv_xchange.h
index f59791fea9..0e098f7c67 100644
--- a/modules/service/xchange/include/dap_chain_net_srv_xchange.h
+++ b/modules/service/xchange/include/dap_chain_net_srv_xchange.h
@@ -71,4 +71,4 @@ typedef struct dap_chain_net_srv_xchange {
 
 int dap_chain_net_srv_xchange_init();
 void dap_chain_net_srv_xchange_deinit();
-bool dap_chain_net_srv_xchange_verificator(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx);
+bool dap_chain_net_srv_xchange_verificator(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx, bool a_owner);
diff --git a/modules/type/blocks/dap_chain_cs_blocks.c b/modules/type/blocks/dap_chain_cs_blocks.c
index 276a349df5..7d2b74322d 100644
--- a/modules/type/blocks/dap_chain_cs_blocks.c
+++ b/modules/type/blocks/dap_chain_cs_blocks.c
@@ -623,8 +623,7 @@ static int  s_add_atom_to_ledger(dap_chain_cs_blocks_t * a_blocks, dap_ledger_t
                 l_ret=dap_chain_ledger_token_load(a_ledger, l_token, l_datum->header.data_size);
             } break;
             case DAP_CHAIN_DATUM_TOKEN_EMISSION: {
-                dap_chain_datum_token_emission_t *l_token_emission = (dap_chain_datum_token_emission_t*) l_datum->data;
-                l_ret=dap_chain_ledger_token_emission_load(a_ledger, l_token_emission, l_datum->header.data_size);
+                l_ret=dap_chain_ledger_token_emission_load(a_ledger, l_datum->data, l_datum->header.data_size);
             } break;
             case DAP_CHAIN_DATUM_TX: {
                 dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*) l_datum->data;
diff --git a/modules/type/dag/dap_chain_cs_dag.c b/modules/type/dag/dap_chain_cs_dag.c
index 6309d93a2d..837281f0b6 100644
--- a/modules/type/dag/dap_chain_cs_dag.c
+++ b/modules/type/dag/dap_chain_cs_dag.c
@@ -304,8 +304,7 @@ static int s_dap_chain_add_atom_to_ledger(dap_chain_cs_dag_t * a_dag, dap_ledger
         }
         break;
         case DAP_CHAIN_DATUM_TOKEN_EMISSION: {
-            dap_chain_datum_token_emission_t *l_token_emission = (dap_chain_datum_token_emission_t*) l_datum->data;
-            return dap_chain_ledger_token_emission_load(a_ledger, l_token_emission, l_datum->header.data_size);
+            return dap_chain_ledger_token_emission_load(a_ledger, l_datum->data, l_datum->header.data_size);
         }
         break;
         case DAP_CHAIN_DATUM_TX: {
-- 
GitLab