From bd31210f55efd023cb67af834a3468ea48e2e933 Mon Sep 17 00:00:00 2001
From: Roman Khlopkov <roman.khlopkov@demlabs.net>
Date: Wed, 12 Mar 2025 04:58:46 +0000
Subject: [PATCH] port-15147

---
 modules/datum/dap_chain_datum_tx_voting.c     |  33 +-
 .../datum/include/dap_chain_datum_tx_tsd.h    |   1 +
 .../datum/include/dap_chain_datum_tx_voting.h |   9 +-
 modules/ledger/dap_chain_ledger.c             | 290 ++++-----
 modules/ledger/dap_chain_ledger_tx.c          | 575 +++++++++++-------
 modules/ledger/include/dap_chain_ledger.h     |  48 +-
 modules/net/dap_chain_net_tx.c                |  12 +-
 modules/net/dap_chain_node.c                  | 100 ++-
 modules/node-cli/dap_chain_node_cli_cmd_tx.c  |  22 +-
 .../dap_chain_net_srv_stake_pos_delegate.c    |   9 +-
 .../service/voting/dap_chain_net_srv_voting.c | 387 ++++++------
 .../voting/include/dap_chain_net_srv_voting.h |  11 +-
 12 files changed, 832 insertions(+), 665 deletions(-)

diff --git a/modules/datum/dap_chain_datum_tx_voting.c b/modules/datum/dap_chain_datum_tx_voting.c
index cc09e07e1e..901a1cac67 100644
--- a/modules/datum/dap_chain_datum_tx_voting.c
+++ b/modules/datum/dap_chain_datum_tx_voting.c
@@ -31,7 +31,7 @@ dap_chain_datum_tx_voting_params_t *dap_chain_datum_tx_voting_parse_tsd(dap_chai
 {
     if (!a_tx)
         return NULL;
-    dap_chain_datum_tx_voting_params_t *l_voting_parms = DAP_NEW_Z(dap_chain_datum_tx_voting_params_t);
+    dap_chain_datum_tx_voting_params_t *l_voting_params = DAP_NEW_Z(dap_chain_datum_tx_voting_params_t);
     char *l_buf_string;
     byte_t *l_item; size_t l_tx_item_size;
     TX_ITEM_ITER_TX(l_item, l_tx_item_size, a_tx) {
@@ -41,29 +41,31 @@ dap_chain_datum_tx_voting_params_t *dap_chain_datum_tx_voting_parse_tsd(dap_chai
         switch(l_tsd->type){
         case VOTING_TSD_TYPE_QUESTION:
             l_buf_string = DAP_NEW_Z_SIZE(char, l_tsd->size + 1);
-            l_voting_parms->question = memcpy(l_buf_string, l_tsd->data, l_tsd->size);
+            l_voting_params->question = memcpy(l_buf_string, l_tsd->data, l_tsd->size);
             break;
         case VOTING_TSD_TYPE_OPTION:
             l_buf_string = DAP_NEW_Z_SIZE(char, l_tsd->size + 1);
-            l_voting_parms->options = dap_list_append(l_voting_parms->options, memcpy(l_buf_string, l_tsd->data, l_tsd->size));
+            l_voting_params->options = dap_list_append(l_voting_params->options, memcpy(l_buf_string, l_tsd->data, l_tsd->size));
             break;
         case VOTING_TSD_TYPE_EXPIRE:
-            l_voting_parms->voting_expire = *(dap_time_t*)l_tsd->data;
+            l_voting_params->voting_expire = *(dap_time_t*)l_tsd->data;
             break;
         case VOTING_TSD_TYPE_MAX_VOTES_COUNT:
-            l_voting_parms->votes_max_count = *(uint64_t*)l_tsd->data;
+            l_voting_params->votes_max_count = *(uint64_t*)l_tsd->data;
             break;
         case VOTING_TSD_TYPE_DELEGATED_KEY_REQUIRED:
-            l_voting_parms->delegate_key_required = *l_tsd->data;
+            l_voting_params->delegate_key_required = *l_tsd->data;
             break;
         case VOTING_TSD_TYPE_VOTE_CHANGING_ALLOWED:
-            l_voting_parms->vote_changing_allowed = *l_tsd->data;
+            l_voting_params->vote_changing_allowed = *l_tsd->data;
             break;
+        case VOTING_TSD_TYPE_TOKEN:
+            strcpy(l_voting_params->token_ticker, (char *)l_tsd->data);
         default:
             break;
         }
     }
-    return l_voting_parms;
+    return l_voting_params;
 }
 
 void dap_chain_datum_tx_voting_params_delete(dap_chain_datum_tx_voting_params_t *a_params)
@@ -131,6 +133,18 @@ dap_chain_tx_tsd_t* dap_chain_datum_voting_vote_changing_allowed_tsd_create(bool
     return l_tsd;
 }
 
+dap_chain_tx_tsd_t *dap_chain_datum_voting_token_tsd_create(const char *a_token_ticker)
+{
+    dap_return_val_if_fail(a_token_ticker && *a_token_ticker, NULL);
+    size_t l_ticker_len = strlen(a_token_ticker);
+    if (l_ticker_len >= DAP_CHAIN_TICKER_SIZE_MAX) {
+        log_it(L_ERROR, "Ticker len %zu is too big", l_ticker_len);
+        return NULL;
+    }
+    dap_chain_tx_tsd_t *l_tsd = dap_chain_datum_tx_item_tsd_create((char *)a_token_ticker, VOTING_TSD_TYPE_TOKEN, l_ticker_len);
+    return l_tsd;
+}
+
 dap_chain_tx_tsd_t* dap_chain_datum_voting_vote_tx_cond_tsd_create(dap_chain_hash_fast_t a_tx_hash, int a_out_idx)
 {
     dap_chain_tx_voting_tx_cond_t l_temp = {
@@ -184,6 +198,9 @@ json_object *dap_chain_datum_tx_item_voting_tsd_to_json(dap_chain_datum_tx_t* a_
         case VOTING_TSD_TYPE_OPTION:
             json_object_array_add(l_answer_array_object, json_object_new_string_len((char*)l_tsd->data, l_tsd->size));
             break;
+        case VOTING_TSD_TYPE_TOKEN:
+            json_object_object_add(l_object, "token", json_object_new_string_len((char*)l_tsd->data, l_tsd->size));
+            break;
         case VOTING_TSD_TYPE_EXPIRE:
             json_object_object_add(l_object, "exired", json_object_new_uint64(*(uint64_t*)l_tsd->data));
             break;
diff --git a/modules/datum/include/dap_chain_datum_tx_tsd.h b/modules/datum/include/dap_chain_datum_tx_tsd.h
index d63d96d244..f1faa200d6 100644
--- a/modules/datum/include/dap_chain_datum_tx_tsd.h
+++ b/modules/datum/include/dap_chain_datum_tx_tsd.h
@@ -7,6 +7,7 @@
 #define DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TICKER         0xf001
 #define DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TX_HASH        0xf002
 #define DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TRACKER        0xf0fa
+#define DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_VOTING_HASH    0xf0fe
 
 typedef struct dap_chain_tx_tsd {
     struct {
diff --git a/modules/datum/include/dap_chain_datum_tx_voting.h b/modules/datum/include/dap_chain_datum_tx_voting.h
index e2376921e2..264b536140 100644
--- a/modules/datum/include/dap_chain_datum_tx_voting.h
+++ b/modules/datum/include/dap_chain_datum_tx_voting.h
@@ -39,7 +39,8 @@ typedef enum dap_chain_datum_voting_tsd_type {
     VOTING_TSD_TYPE_DELEGATED_KEY_REQUIRED,
     VOTING_TSD_TYPE_VOTE_CHANGING_ALLOWED,
     VOTING_TSD_TYPE_VOTE_TX_COND,
-    VOTING_TSD_TYPE_VOTE
+    VOTING_TSD_TYPE_TOKEN,
+    VOTING_TSD_TYPE_VOTE = 0xfa // hardfork related
 } dap_chain_datum_voting_tsd_type_t;
 
 typedef struct dap_chain_tx_voting {
@@ -58,12 +59,13 @@ typedef struct dap_chain_tx_vote {
 } DAP_ALIGN_PACKED dap_chain_tx_vote_t;
 
 typedef struct dap_chain_datum_tx_voting_params {
-    char *question;
+    char       *question;
     dap_list_t *options;
     dap_time_t voting_expire;
     uint64_t   votes_max_count;
     bool       delegate_key_required;
     bool       vote_changing_allowed;
+    char       token_ticker[DAP_CHAIN_TICKER_SIZE_MAX];
 } dap_chain_datum_tx_voting_params_t;
 
 #ifdef __cplusplus
@@ -80,6 +82,7 @@ dap_chain_tx_tsd_t* dap_chain_datum_voting_max_votes_count_tsd_create(uint64_t a
 dap_chain_tx_tsd_t *dap_chain_datum_voting_delegated_key_required_tsd_create(bool a_delegated_key_required);
 dap_chain_tx_tsd_t* dap_chain_datum_voting_vote_changing_allowed_tsd_create(bool a_vote_changing_allowed);
 dap_chain_tx_tsd_t* dap_chain_datum_voting_vote_tx_cond_tsd_create(dap_chain_hash_fast_t a_tx_hash, int a_out_idx);
+dap_chain_tx_tsd_t *dap_chain_datum_voting_token_tsd_create(const char *a_token_ticker);
 
 dap_chain_tx_voting_t *dap_chain_datum_tx_item_voting_create(void);
 json_object *dap_chain_datum_tx_item_voting_tsd_to_json(dap_chain_datum_tx_t* a_tx);
@@ -91,4 +94,4 @@ char *dap_chain_datum_tx_voting_get_answer_text_by_idx(dap_chain_datum_tx_t *a_t
 
 #ifdef __cplusplus
 }
-#endif
\ No newline at end of file
+#endif
diff --git a/modules/ledger/dap_chain_ledger.c b/modules/ledger/dap_chain_ledger.c
index fe17bfa4da..2249b14b5e 100644
--- a/modules/ledger/dap_chain_ledger.c
+++ b/modules/ledger/dap_chain_ledger.c
@@ -1171,51 +1171,6 @@ uint256_t dap_ledger_calc_balance(dap_ledger_t *a_ledger, const dap_chain_addr_t
     return false;
  }
 
-/**
- * Get the transaction in the cache by the public key that signed the transaction,
- * starting from the next hash after a_tx_first_hash
- *
- * a_public_key[in] public key that signed the transaction
- * a_public_key_size[in] public key size
- * a_tx_first_hash [in/out] hash of the initial transaction/ found transaction, if 0 start from the beginning
- */
-const dap_chain_datum_tx_t* dap_ledger_tx_find_by_pkey(dap_ledger_t *a_ledger,
-        char *a_public_key, size_t a_public_key_size, dap_chain_hash_fast_t *a_tx_first_hash)
-{
-    if(!a_public_key || !a_tx_first_hash)
-        return NULL;
-    dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger);
-    dap_chain_datum_tx_t *l_cur_tx = NULL;
-    bool is_null_hash = dap_hash_fast_is_blank(a_tx_first_hash);
-    bool is_search_enable = is_null_hash;
-    dap_ledger_tx_item_t *l_iter_current, *l_item_tmp;
-    pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock);
-    HASH_ITER(hh, l_ledger_pvt->ledger_items , l_iter_current, l_item_tmp) {
-        dap_chain_datum_tx_t *l_tx_tmp = l_iter_current->tx;
-        dap_chain_hash_fast_t *l_tx_hash_tmp = &l_iter_current->tx_hash_fast;
-        // start searching from the next hash after a_tx_first_hash
-        if (!is_search_enable) {
-            is_search_enable = dap_hash_fast_compare(l_tx_hash_tmp, a_tx_first_hash);
-            continue;
-        }
-        // Get sign item from transaction
-        dap_chain_tx_sig_t *l_tx_sig = (dap_chain_tx_sig_t*) dap_chain_datum_tx_item_get(l_tx_tmp, NULL,
-                NULL, TX_ITEM_TYPE_SIG, NULL);
-        // Get dap_sign_t from item
-        dap_sign_t *l_sig = dap_chain_datum_tx_item_sig_get_sign(l_tx_sig);
-        if(l_sig) {
-            // compare public key in transaction with a_public_key
-            if(a_public_key_size == l_sig->header.sign_pkey_size &&
-                    !memcmp(a_public_key, l_sig->pkey_n_sign, a_public_key_size)) {
-                l_cur_tx = l_tx_tmp;
-                memcpy(a_tx_first_hash, l_tx_hash_tmp, sizeof(dap_chain_hash_fast_t));
-                break;
-            }
-        }
-    }
-    pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock);
-    return l_cur_tx;
-}
 /**
  * @brief dap_ledger_find_pkey_by_hash
  * @param a_ledger to search
@@ -1274,92 +1229,6 @@ dap_list_t* dap_ledger_tx_cache_find_out_cond_all(dap_ledger_t *a_ledger, dap_ch
     return l_ret;
 }
 
-/**
- * Get the transaction in the cache with the out_cond item
- *
- * a_addr[in] wallet address, whose owner can use the service
- */
-dap_chain_datum_tx_t* dap_ledger_tx_cache_find_out_cond(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_cond_type,
-        dap_chain_hash_fast_t *a_tx_first_hash, dap_chain_tx_out_cond_t **a_out_cond, int *a_out_cond_idx, char *a_token_ticker)
-{
-    if (!a_tx_first_hash)
-        return NULL;
-    dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger);
-    dap_chain_datum_tx_t *l_cur_tx = NULL;
-    bool is_null_hash = dap_hash_fast_is_blank(a_tx_first_hash);
-    bool is_search_enable = is_null_hash;
-    dap_ledger_tx_item_t *l_iter_current = NULL, *l_item_tmp = NULL;
-    dap_chain_tx_out_cond_t *l_tx_out_cond = NULL;
-    int l_tx_out_cond_idx = 0;
-    pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock);
-    HASH_ITER(hh, l_ledger_pvt->ledger_items, l_iter_current, l_item_tmp) {
-        dap_chain_datum_tx_t *l_tx_tmp = l_iter_current->tx;
-        dap_chain_hash_fast_t *l_tx_hash_tmp = &l_iter_current->tx_hash_fast;
-        // start searching from the next hash after a_tx_first_hash
-        if (!is_search_enable) {
-            is_search_enable = dap_hash_fast_compare(l_tx_hash_tmp, a_tx_first_hash);
-            continue;
-        }
-        // Get out_cond item from transaction
-        l_tx_out_cond = dap_chain_datum_tx_out_cond_get(l_tx_tmp, a_cond_type, &l_tx_out_cond_idx);
-
-        if(l_tx_out_cond) {
-            l_cur_tx = l_tx_tmp;
-            memcpy(a_tx_first_hash, l_tx_hash_tmp, sizeof(dap_chain_hash_fast_t));
-            if (a_token_ticker) {
-                strcpy(a_token_ticker, l_iter_current->cache_data.token_ticker);
-            }
-            break;
-        }
-    }
-    pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock);
-    if (a_out_cond) {
-        *a_out_cond = l_tx_out_cond;
-    }
-    if (a_out_cond_idx) {
-        *a_out_cond_idx = l_tx_out_cond_idx;
-    }
-    return l_cur_tx;
-}
-
-/**
- * Get the value from all transactions in the cache with out_cond item
- *
- * a_addr[in] wallet address, whose owner can use the service
- * a_sign [in] signature of a_addr hash for check valid key
- * a_sign_size [in] signature size
- *
- * a_public_key[in] public key that signed the transaction
- * a_public_key_size[in] public key size
- */
-uint256_t dap_ledger_tx_cache_get_out_cond_value(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_cond_type,
-                                                       dap_chain_addr_t *a_addr, dap_chain_tx_out_cond_t **tx_out_cond)
-
-{
-    uint256_t l_ret_value = {};
-
-    dap_chain_datum_tx_t *l_tx_tmp;
-    dap_chain_hash_fast_t l_tx_first_hash = { 0 }; // start hash
-    /* size_t l_pub_key_size = a_key_from->pub_key_data_size;
-     uint8_t *l_pub_key = dap_enc_key_serialize_pub_key(a_key_from, &l_pub_key_size);*/
-    dap_chain_tx_out_cond_t *l_tx_out_cond;
-    // Find all transactions
-    do {
-        l_tx_tmp = dap_ledger_tx_cache_find_out_cond(a_ledger, a_cond_type, &l_tx_first_hash, &l_tx_out_cond, NULL, NULL);
-        // Get out_cond item from transaction
-        if(l_tx_tmp) {
-            UNUSED(a_addr);
-            // TODO check relations a_addr with cond_data and public key
-            if(l_tx_out_cond) {
-                l_ret_value = l_tx_out_cond->header.value;
-                if(tx_out_cond)
-                    *tx_out_cond = l_tx_out_cond;
-            }
-        }
-    } while(l_tx_tmp);
-    return l_ret_value;
-}
-
 dap_list_t *dap_ledger_get_list_tx_outs(dap_ledger_t *a_ledger, const char *a_token_ticker, const dap_chain_addr_t *a_addr_from,
                                         uint256_t *a_value_transfer)
 {
@@ -1411,7 +1280,8 @@ dap_list_t *dap_ledger_get_list_tx_outs(dap_ledger_t *a_ledger, const char *a_to
             }
         }
     }
-    if (a_value_transfer) *a_value_transfer = l_value_transfer;
+    if (a_value_transfer)
+        *a_value_transfer = l_value_transfer;
     return l_list_used_out;
 }
 
@@ -1498,7 +1368,7 @@ dap_chain_datum_tx_t *dap_ledger_datum_iter_get_last(dap_ledger_datum_iter_t *a_
 {
     dap_ledger_private_t *l_ledger_pvt = PVT(a_iter->net->pub.ledger);
     pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock);
-    a_iter->cur_ledger_tx_item = l_ledger_pvt->ledger_items->hh.prev;
+    a_iter->cur_ledger_tx_item = HASH_LAST(l_ledger_pvt->ledger_items);
     a_iter->cur = ((dap_ledger_tx_item_t *)(a_iter->cur_ledger_tx_item))->tx;
     a_iter->cur_hash = ((dap_ledger_tx_item_t *)(a_iter->cur_ledger_tx_item))->tx_hash_fast;
     a_iter->is_unspent = ((dap_ledger_tx_item_t *)(a_iter->cur_ledger_tx_item))->cache_data.ts_spent ? false : true;
@@ -1531,69 +1401,125 @@ dap_chain_tx_used_out_item_t *dap_ledger_get_tx_cond_out(dap_ledger_t *a_ledger,
 
 
 /**
- * @brief dap_ledger_get_list_tx_cond_outs_with_val
- * @param a_ledger
- * @param a_token_ticker
- * @param a_addr_from
- * @param a_subtype
- * @param a_value_need
- * @param a_value_transfer
- * @return
+ * Get the transaction in the cache by the addr in sig item
+ *
+ * a_addr[in] public key that signed the transaction
+ * a_tx_first_hash [in/out] hash of the initial transaction/ found transaction, if 0 start from the beginning
  */
-dap_list_t *dap_ledger_get_list_tx_cond_outs_with_val(dap_ledger_t *a_ledger, const char *a_token_ticker,  const dap_chain_addr_t *a_addr_from,
-        dap_chain_tx_out_cond_subtype_t a_subtype, uint256_t a_value_need, uint256_t *a_value_transfer)
+
+dap_chain_tx_out_cond_t *dap_ledger_out_cond_unspent_find_by_addr(dap_ledger_t *a_ledger, const char *a_token, dap_chain_tx_out_cond_subtype_t a_subtype,
+                                                                  const dap_chain_addr_t *a_addr, dap_chain_hash_fast_t *a_tx_first_hash, int *a_out_idx)
 {
-    dap_list_t *l_list_used_out = NULL; // list of transaction with 'out' items
-    dap_chain_hash_fast_t l_tx_cur_hash = { 0 };
-    uint256_t l_value_transfer = { };
-    dap_chain_datum_tx_t *l_tx;
-    while( compare256(l_value_transfer, a_value_need) == -1 
-        && ( l_tx = dap_ledger_tx_find_by_addr(a_ledger, a_token_ticker, a_addr_from, &l_tx_cur_hash)) )
-    {
-        byte_t *it; size_t l_size; int i, l_out_idx_tmp = -1;
-        TX_ITEM_ITER_TX_TYPE(it, TX_ITEM_TYPE_OUT_COND, l_size, i, l_tx) {
-            ++l_out_idx_tmp;
-            dap_chain_tx_out_cond_t *l_out_cond = (dap_chain_tx_out_cond_t*) it;
-            if (a_subtype != l_out_cond->header.subtype || IS_ZERO_256(l_out_cond->header.value) )
-                continue;
-            dap_chain_tx_used_out_item_t *l_item = DAP_NEW_Z(dap_chain_tx_used_out_item_t);
-            *l_item = (dap_chain_tx_used_out_item_t) { l_tx_cur_hash, (uint32_t)l_out_idx_tmp, l_out_cond->header.value };
-            l_list_used_out = dap_list_append(l_list_used_out, l_item);
-            SUM_256_256(l_value_transfer, l_item->value, &l_value_transfer);
-            // already accumulated the required value, finish the search for 'out' items
-            if ( compare256(l_value_transfer, a_value_need) != -1 ) {
-                break;
+    if (!a_addr || !a_token)
+        return NULL;
+    dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger);
+    dap_chain_tx_out_cond_t *ret = NULL;
+    dap_ledger_tx_item_t *l_iter_start = NULL, *it;
+    pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock);
+    if (a_tx_first_hash && !dap_hash_fast_is_blank(a_tx_first_hash)) {
+        HASH_FIND(hh, l_ledger_pvt->ledger_items, a_tx_first_hash, sizeof(dap_hash_t), l_iter_start);
+        if (!l_iter_start || !l_iter_start->hh.next) {
+            pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock);
+            return NULL;
+        }
+        // start searching from the next hash after a_tx_first_hash
+        l_iter_start = l_iter_start->hh.next;
+    } else
+        l_iter_start = l_ledger_pvt->ledger_items;
+    for (it = l_iter_start; it; it = it->hh.next, ret = NULL) {
+        // If a_token is setup we check if its not our token - miss it
+        if (*it->cache_data.token_ticker && dap_strcmp(it->cache_data.token_ticker, a_token))
+            continue;
+        ret = NULL;
+        // Get 'out_cond' item from transaction
+        byte_t *l_item; size_t l_size; int i, l_out_idx = 0;
+        TX_ITEM_ITER_TX_TYPE(l_item, TX_ITEM_TYPE_OUT_ALL, l_size, i, it->tx) {
+            if (*l_item == TX_ITEM_TYPE_OUT_COND) {
+                dap_chain_tx_out_cond_subtype_t l_subtype = ((dap_chain_tx_out_cond_t *)l_item)->header.subtype;
+                if (l_subtype == a_subtype ||
+                        (a_subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_ALL && l_subtype != DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE)) {
+                    ret = (dap_chain_tx_out_cond_t *)l_item;
+                    break;
+                }
             }
+            l_out_idx++;
+        }
+        // Don't return regular tx or spent conditions
+        if (!ret || !dap_hash_fast_is_blank(&it->out_metadata[l_out_idx].tx_spent_hash_fast))
+            continue;
+        dap_hash_fast_t l_owner_tx_hash = dap_ledger_get_first_chain_tx_hash(a_ledger, a_subtype, &it->tx_hash_fast);
+        dap_chain_datum_tx_t *l_tx = dap_hash_fast_is_blank(&l_owner_tx_hash) ? it->tx
+                                                                              : dap_ledger_tx_find_by_hash(a_ledger, &l_owner_tx_hash);
+        if (!l_tx) {
+            log_it(L_ERROR, "Can't find owner for tx %s", dap_hash_fast_to_str_static(&it->tx_hash_fast));
+            continue;
+        }
+        // Get sign item from transaction
+        dap_chain_tx_sig_t *l_tx_sig = (dap_chain_tx_sig_t*) dap_chain_datum_tx_item_get(l_tx, NULL, NULL, TX_ITEM_TYPE_SIG, NULL);
+        // Get dap_sign_t from item
+        dap_sign_t *l_sign = dap_chain_datum_tx_item_sig_get_sign(l_tx_sig);
+        // compare public key in transaction with a_public_key
+        dap_chain_hash_fast_t l_sign_hash = {};
+        dap_sign_get_pkey_hash(l_sign, &l_sign_hash);
+        if (dap_hash_fast_compare(&l_sign_hash, &a_addr->data.hash_fast)) {
+            if (a_tx_first_hash)
+                *a_tx_first_hash = it->tx_hash_fast;
+            if (a_out_idx)
+                *a_out_idx = l_out_idx;
+            break;
         }
     }
-    return compare256(l_value_transfer, a_value_need) > -1 && l_list_used_out
-        ? ({ if (a_value_transfer) *a_value_transfer = l_value_transfer; l_list_used_out; })
-        : ( dap_list_free_full(l_list_used_out, NULL), NULL );
+    pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock);
+    return ret;
 }
 
-dap_list_t *dap_ledger_get_list_tx_cond_outs(dap_ledger_t *a_ledger, const dap_chain_addr_t *a_addr_from)
+dap_list_t *dap_ledger_get_list_tx_cond_outs(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_subtype,
+                                             const char *a_token_ticker,  const dap_chain_addr_t *a_addr_from)
 {
     dap_list_t *l_list_used_out = NULL; // list of transaction with 'out' items
     dap_chain_hash_fast_t l_tx_cur_hash = { };
-    dap_chain_datum_tx_t *l_tx;
-    while(( l_tx = dap_ledger_tx_find_by_addr(a_ledger, a_ledger->net->pub.native_ticker, a_addr_from, &l_tx_cur_hash) )) {
-        byte_t *it; size_t l_size; int i, l_out_idx_tmp = -1;
-        TX_ITEM_ITER_TX_TYPE(it, TX_ITEM_TYPE_OUT_COND, l_size, i, l_tx) {
-            ++l_out_idx_tmp;
-            dap_chain_tx_out_cond_t *l_out_cond = (dap_chain_tx_out_cond_t *)it;
-            if ( DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE == l_out_cond->header.subtype || IS_ZERO_256(l_out_cond->header.value) )
-                continue;
-            if (dap_ledger_tx_hash_is_used_out_item(a_ledger, &l_tx_cur_hash, l_out_idx_tmp, NULL))
-                continue;   // TODO Move this check to dap_ledger_tx_find_by_addr() to avoid double search
-            dap_chain_tx_used_out_item_t *l_item = DAP_NEW_Z(dap_chain_tx_used_out_item_t);
-            *l_item = (dap_chain_tx_used_out_item_t) { l_tx_cur_hash, (uint32_t)l_out_idx_tmp, l_out_cond->header.value };
-            l_list_used_out = dap_list_append(l_list_used_out, l_item);
-        }
+    int l_out_idx;
+    dap_chain_tx_out_cond_t *l_cond;
+    while ( (l_cond = dap_ledger_out_cond_unspent_find_by_addr(a_ledger, a_token_ticker, a_subtype, a_addr_from, &l_tx_cur_hash, &l_out_idx)) ) {
+        dap_chain_tx_used_out_item_t *l_item = DAP_NEW_Z(dap_chain_tx_used_out_item_t);
+        *l_item = (dap_chain_tx_used_out_item_t) { l_tx_cur_hash, (uint32_t)l_out_idx, l_cond->header.value };
+        l_list_used_out = dap_list_append(l_list_used_out, l_item);
     }
-
     return l_list_used_out;
 }
 
+bool dap_ledger_check_condition_owner(dap_ledger_t *a_ledger, dap_hash_fast_t *a_tx_hash, dap_chain_tx_out_cond_subtype_t a_cond_subtype,
+                                      int a_out_idx, dap_sign_t *a_owner_sign)
+{
+    dap_return_val_if_fail(a_ledger && a_tx_hash && a_owner_sign, false);
+    // Get first tx
+    dap_chain_datum_tx_t *l_check_tx = dap_ledger_tx_find_by_hash(a_ledger, a_tx_hash);
+    if (!l_check_tx) {
+        log_it(L_ERROR, "Can't find tx %s", dap_hash_fast_to_str_static(a_tx_hash));
+        return false;
+    }
+    if (!dap_chain_datum_tx_out_cond_get(l_check_tx, a_cond_subtype, NULL)) {
+        log_it(L_ERROR, "Can't find owner for tx %s", dap_hash_fast_to_str_static(a_tx_hash));
+        return false;
+    }
+    dap_hash_fast_t l_first_tx_hash = dap_ledger_get_first_chain_tx_hash(a_ledger, a_cond_subtype, a_tx_hash);
+    dap_chain_datum_tx_t *l_first_tx = dap_hash_fast_is_blank(&l_first_tx_hash) ? l_check_tx
+                                                                                : dap_ledger_tx_find_by_hash(a_ledger, &l_first_tx_hash);
+    if (!l_first_tx) {
+        log_it(L_ERROR, "Can't find owner tx %s", dap_hash_fast_to_str_static(&l_first_tx_hash));
+        return false;
+    }
+    dap_chain_tx_sig_t *l_first_tx_sig = (dap_chain_tx_sig_t *)dap_chain_datum_tx_item_get(l_first_tx, NULL, NULL, TX_ITEM_TYPE_SIG, NULL);
+    dap_sign_t *l_sign = dap_chain_datum_tx_item_sig_get_sign((dap_chain_tx_sig_t *)l_first_tx_sig);
+    if (!l_sign) {
+        log_it(L_ERROR, "Can't find signature for tx %s", dap_hash_fast_to_str_static(&l_first_tx_hash));
+        return false;
+    }
+    return dap_sign_compare_pkeys(a_owner_sign, l_sign);
+}
+
+
+
 bool dap_ledger_cache_enabled(dap_ledger_t *a_ledger)
 {
     return is_ledger_cached(PVT(a_ledger));
diff --git a/modules/ledger/dap_chain_ledger_tx.c b/modules/ledger/dap_chain_ledger_tx.c
index 1706654159..bdfbb6d2d2 100644
--- a/modules/ledger/dap_chain_ledger_tx.c
+++ b/modules/ledger/dap_chain_ledger_tx.c
@@ -44,6 +44,7 @@ typedef struct dap_ledger_verificator {
 
 typedef struct dap_chain_ledger_votings_callbacks {
     dap_ledger_voting_callback_t voting_callback;
+    dap_ledger_vote_callback_t vote_callback;
     dap_ledger_voting_delete_callback_t voting_delete_callback;
     dap_ledger_voting_expire_callback_t voting_expire_callback;
 } dap_chain_ledger_votings_callbacks_t;
@@ -57,7 +58,7 @@ static dap_ledger_tax_callback_t s_tax_callback = NULL;
 typedef struct dap_ledger_tokenizer {
     char token_ticker[DAP_CHAIN_TICKER_SIZE_MAX];
     uint256_t sum;
-    UT_hash_handle hh;
+    struct dap_ledger_tokenizer *next;
 } dap_ledger_tokenizer_t;
 
 typedef struct dap_ledger_tx_bound {
@@ -105,10 +106,9 @@ typedef struct dap_ledger_cache_gdb_record {
     uint8_t data[];
 } DAP_ALIGN_PACKED dap_ledger_cache_gdb_record_t;
 
-static dap_ledger_tx_item_t *s_tx_item_find_by_addr(dap_ledger_t *a_ledger, const dap_chain_addr_t *a_addr, const char * a_token, dap_chain_hash_fast_t *a_tx_first_hash);
 static bool s_ledger_tx_hash_is_used_out_item(dap_ledger_tx_item_t *a_item, uint32_t a_idx_out, dap_hash_fast_t *a_out_spender_hash);
 static dap_ledger_stake_lock_item_t *s_emissions_for_stake_lock_item_find(dap_ledger_t *a_ledger, const dap_chain_hash_fast_t *a_token_emission_hash);
-
+void dap_ledger_colour_clear_callback(void *a_list_data);
 static dap_chain_datum_tx_t *s_tx_find_by_hash(dap_ledger_t *a_ledger, const dap_chain_hash_fast_t *a_tx_hash, dap_ledger_tx_item_t **a_item_out, bool a_unspent_only);
 static struct json_object *s_wallet_info_json_collect(dap_ledger_t *a_ledger, dap_ledger_wallet_balance_t* a_bal);
 
@@ -214,6 +214,22 @@ bool dap_ledger_is_used_reward(dap_ledger_t *a_ledger, dap_hash_fast_t *a_block_
     return s_find_reward(a_ledger, &l_search_key);
 }
 
+dap_ledger_tokenizer_t *s_tokenizer_find(dap_ledger_tokenizer_t *a_list, const char *a_ticker)
+{
+    for (dap_ledger_tokenizer_t *it = a_list; it; it = it->next)
+        if (!dap_strcmp(it->token_ticker, a_ticker))
+            return it;
+    return NULL;
+}
+
+size_t s_tokenizer_count(dap_ledger_tokenizer_t *a_list)
+{
+    size_t ret = 0;
+    for (dap_ledger_tokenizer_t *it = a_list; it; it = it->next)
+        ret++;
+    return ret;
+}
+
 /**
  * Checking a new transaction before adding to the cache
  *
@@ -227,6 +243,7 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
                             dap_list_t **a_list_bound_items,
                             dap_list_t **a_list_tx_out,
                             char *a_main_ticker,
+                            dap_ledger_tokenizer_t **a_values_from_cur_tx,
                             dap_chain_srv_uid_t *a_tag,
                             dap_chain_tx_tag_action_type_t *a_action,
                             bool a_check_for_removing)
@@ -775,7 +792,7 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
                 break;
             }
         }
-        HASH_FIND_STR(l_values_from_prev_tx, l_token, l_value_cur);
+        l_value_cur = s_tokenizer_find(l_values_from_prev_tx, l_token);
         if (!l_value_cur) {
             l_value_cur = DAP_NEW_Z(dap_ledger_tokenizer_t);
             if ( !l_value_cur ) {
@@ -783,8 +800,8 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
                 l_err_num = DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY;
                 break;
             }
-            strcpy(l_value_cur->token_ticker, l_token);
-            HASH_ADD_STR(l_values_from_prev_tx, token_ticker, l_value_cur);
+            dap_strncpy(l_value_cur->token_ticker, l_token, DAP_CHAIN_TICKER_SIZE_MAX);
+            LL_APPEND(l_values_from_prev_tx, l_value_cur);
         }
         // calculate  from previous transactions per each token
         if (SUM_256_256(l_value_cur->sum, l_value, &l_value_cur->sum)) {
@@ -800,32 +817,28 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
     if (l_err_num) {
         if ( l_list_bound_items )
             dap_list_free_full(l_list_bound_items, NULL);
-        HASH_ITER(hh, l_values_from_prev_tx, l_value_cur, l_tmp) {
-            HASH_DEL(l_values_from_prev_tx, l_value_cur);
+        LL_FOREACH_SAFE(l_values_from_prev_tx, l_value_cur, l_tmp)
             DAP_DELETE(l_value_cur);
-        }
         return l_err_num;
     }
 
     // 6. Compare sum of values in 'out' items
     if ( !l_main_ticker )
-        switch ( HASH_COUNT(l_values_from_prev_tx) ) {
+        switch ( s_tokenizer_count(l_values_from_prev_tx) ) {
         case 1:
             l_main_ticker = l_value_cur->token_ticker;
             break;
         case 2:
-            HASH_FIND_STR(l_values_from_prev_tx, a_ledger->net->pub.native_ticker, l_value_cur);
+            l_value_cur = s_tokenizer_find(l_values_from_prev_tx, a_ledger->net->pub.native_ticker);
             if (l_value_cur) {
-                l_value_cur = l_value_cur->hh.next ? l_value_cur->hh.next : l_value_cur->hh.prev;
+                l_value_cur = l_value_cur->next ? l_value_cur->next : l_values_from_prev_tx;
                 l_main_ticker = l_value_cur->token_ticker;
             }
             break;
         default:
             dap_list_free_full(l_list_bound_items, NULL);
-            HASH_ITER(hh, l_values_from_prev_tx, l_value_cur, l_tmp) {
-                HASH_DEL(l_values_from_prev_tx, l_value_cur);
+            LL_FOREACH_SAFE(l_values_from_prev_tx, l_value_cur, l_tmp)
                 DAP_DELETE(l_value_cur);
-            }
             return DAP_LEDGER_TX_CHECK_NO_MAIN_TICKER;
         }
 
@@ -916,7 +929,7 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
 
         if (l_err_num)
             break;
-        HASH_FIND_STR(l_values_from_cur_tx, l_token, l_value_cur);
+        l_value_cur = s_tokenizer_find(l_values_from_cur_tx, l_token);
         if (!l_value_cur) {
             l_value_cur = DAP_NEW_Z(dap_ledger_tokenizer_t);
             if ( !l_value_cur ) {
@@ -925,7 +938,7 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
                 break;
             }
             dap_strncpy(l_value_cur->token_ticker, l_token, sizeof(l_value_cur->token_ticker));
-            HASH_ADD_STR(l_values_from_cur_tx, token_ticker, l_value_cur);    
+            LL_APPEND(l_values_from_cur_tx, l_value_cur);
         }
         if (SUM_256_256(l_value_cur->sum, l_value, &l_value_cur->sum)) {
             debug_if(g_debug_ledger, L_WARNING, "Sum result overflow for tx_add_check with ticker %s",
@@ -958,13 +971,13 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
 
     // Check for transaction consistency (sum(ins) == sum(outs))
     if ( !l_err_num && !dap_ledger_datum_is_enforced(a_ledger, a_tx_hash, true) ) {
-        if ( HASH_COUNT(l_values_from_prev_tx) != HASH_COUNT(l_values_from_cur_tx) ) {
-            log_it(L_ERROR, "Token tickers IN and OUT mismatch: %u != %u",
-                            HASH_COUNT(l_values_from_prev_tx), HASH_COUNT(l_values_from_cur_tx));
+        if ( s_tokenizer_count(l_values_from_prev_tx) != s_tokenizer_count(l_values_from_cur_tx) ) {
+            log_it(L_ERROR, "Token tickers IN and OUT mismatch: %zu != %zu",
+                            s_tokenizer_count(l_values_from_prev_tx), s_tokenizer_count(l_values_from_cur_tx));
             l_err_num = DAP_LEDGER_TX_CHECK_SUM_INS_NOT_EQUAL_SUM_OUTS;
         } else {
-            HASH_ITER(hh, l_values_from_prev_tx, l_value_cur, l_tmp) {
-                HASH_FIND_STR(l_values_from_cur_tx, l_value_cur->token_ticker, l_res);
+            LL_FOREACH(l_values_from_prev_tx, l_value_cur) {
+                l_res = s_tokenizer_find(l_values_from_cur_tx, l_value_cur->token_ticker);
                 if ( !l_res || !EQUAL_256(l_res->sum, l_value_cur->sum) ) {
                     if (g_debug_ledger) {
                         char *l_balance = dap_chain_balance_coins_print(l_res ? l_res->sum : uint256_0), 
@@ -1016,7 +1029,7 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
         // TODO move it to service tag deduction
         if ( dap_chain_datum_tx_item_get(a_tx, NULL, NULL, TX_ITEM_TYPE_VOTING, NULL ) ) {
             if (s_voting_callbacks.voting_callback) {
-                if ((l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTING, a_tx, a_tx_hash, false))) {
+                if ((l_err_num = s_voting_callbacks.voting_callback(a_ledger, a_tx, a_tx_hash, false))) {
                     debug_if(g_debug_ledger, L_WARNING, "Verificator check error %d for voting", l_err_num);
                     l_err_num = DAP_LEDGER_TX_CHECK_VERIFICATOR_CHECK_FAILURE;
                 }
@@ -1028,7 +1041,7 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
                *a_action = DAP_CHAIN_TX_TAG_ACTION_VOTING;
         } else if ( dap_chain_datum_tx_item_get(a_tx, NULL, NULL, TX_ITEM_TYPE_VOTE, NULL) ) {
            if (s_voting_callbacks.voting_callback) {
-               if ((l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTE, a_tx, a_tx_hash, false))) {
+               if ((l_err_num = s_voting_callbacks.vote_callback(a_ledger, a_tx, a_tx_hash, NULL, false))) {
                    debug_if(g_debug_ledger, L_WARNING, "Verificator check error %d for vote", l_err_num);
                    l_err_num = DAP_LEDGER_TX_CHECK_VERIFICATOR_CHECK_FAILURE;
                }
@@ -1044,14 +1057,15 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
     if (a_main_ticker && !l_err_num && (a_main_ticker != l_main_ticker))
         dap_strncpy(a_main_ticker, l_main_ticker, DAP_CHAIN_TICKER_SIZE_MAX);
 
-    HASH_ITER(hh, l_values_from_prev_tx, l_value_cur, l_tmp) {
-        HASH_DEL(l_values_from_prev_tx, l_value_cur);
-        DAP_DELETE(l_value_cur);
-    }
-    HASH_ITER(hh, l_values_from_cur_tx, l_value_cur, l_tmp) {
-        HASH_DEL(l_values_from_cur_tx, l_value_cur);
+    LL_FOREACH_SAFE(l_values_from_prev_tx, l_value_cur, l_tmp)
         DAP_DELETE(l_value_cur);
-    }
+
+    if (!a_values_from_cur_tx || l_err_num) {
+        LL_FOREACH_SAFE(l_values_from_cur_tx, l_value_cur, l_tmp)
+            DAP_DELETE(l_value_cur);
+    } else
+        *a_values_from_cur_tx = l_values_from_cur_tx;
+
     if (!a_list_bound_items || l_err_num) {
         dap_list_free_full(l_list_bound_items, NULL);
     } else {
@@ -1082,7 +1096,7 @@ int dap_ledger_tx_add_check(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx,
         log_it (L_WARNING, "Inconsistent datum TX: datum size %zu != tx size %zu", a_datum_size, l_tx_size);
         return DAP_LEDGER_CHECK_INVALID_SIZE;
     }
-    int l_ret_check = s_tx_cache_check(a_ledger, a_tx, a_datum_hash, false, NULL, NULL, NULL, NULL, NULL, false);
+    int l_ret_check = s_tx_cache_check(a_ledger, a_tx, a_datum_hash, false, NULL, NULL, NULL, NULL, NULL, NULL, false);
     if(g_debug_ledger) {
         if (l_ret_check)
             log_it(L_NOTICE, "Ledger TX adding check not passed for TX %s: error %s",
@@ -1144,19 +1158,33 @@ static int s_balance_cache_update(dap_ledger_t *a_ledger, dap_ledger_wallet_bala
     return 0;
 }
 
+struct tracker_mover_item {
+    dap_hash_fast_t pkey_hash;
+    uint256_t coloured_value;
+    uint256_t cur_value;
+    struct tracker_mover_item *prev, *next;
+};
+
 struct tracker_mover {
     dap_hash_fast_t voting_hash;
-    uint256_t colored_value;
-    uint256_t cur_value;
+    char ticker[DAP_CHAIN_TICKER_SIZE_MAX];
+    struct tracker_mover_item *items;
 };
 
+int s_compare_tracker_items(struct tracker_mover_item *a_item1, struct tracker_mover_item *a_item2)
+{
+    return memcmp(&a_item1->pkey_hash, &a_item1->pkey_hash, sizeof(dap_hash_fast_t));
+}
+
 int s_compare_trackers(dap_list_t *a_tracker1, dap_list_t *a_tracker2)
 {
     struct tracker_mover *l_tracker1 = a_tracker1->data, *l_tracker2 = a_tracker2->data;
     return memcmp(&l_tracker1->voting_hash, &l_tracker2->voting_hash, sizeof(dap_hash_fast_t));
 }
 
-dap_list_t *s_trackers_aggregate(dap_ledger_t *a_ledger, dap_list_t *a_trackers, dap_list_t **a_added, dap_time_t a_ts_creation_time)
+dap_list_t *s_trackers_aggregate(dap_ledger_t *a_ledger, dap_list_t *a_trackers, const char *a_ticker,
+                                 dap_hash_fast_t *a_voting_hash, dap_hash_fast_t *a_pkey_hash,
+                                 dap_list_t **a_added, dap_time_t a_ts_creation_time)
 {
     dap_return_val_if_fail(s_voting_callbacks.voting_expire_callback, a_trackers);
     dap_list_t *it, *tmp;
@@ -1165,24 +1193,37 @@ dap_list_t *s_trackers_aggregate(dap_ledger_t *a_ledger, dap_list_t *a_trackers,
         dap_time_t l_exp_time = s_voting_callbacks.voting_expire_callback(a_ledger, &l_new_tracker->voting_hash);
         if (a_ts_creation_time > l_exp_time) {
             DL_DELETE(*a_added, it);
-            DAP_DEL_MULTY(it->data, it);
+            dap_ledger_colour_clear_callback(it->data);
+            DAP_DELETE(it);
             continue;       // Remove expired colour
         }
         dap_list_t *l_exists = dap_list_find(a_trackers, &l_new_tracker->voting_hash, s_compare_trackers);
         struct tracker_mover *l_exists_tracker = NULL;
-        if (l_exists)
-            l_exists_tracker = l_exists->data;
-        else {
+        if (!l_exists) {
             l_exists_tracker = DAP_NEW_Z_RET_VAL_IF_FAIL(struct tracker_mover, a_trackers);
             l_exists_tracker->voting_hash = l_new_tracker->voting_hash;
-        }
-        if (SUM_256_256(l_exists_tracker->colored_value, l_new_tracker->colored_value, &l_exists_tracker->colored_value)) {
-            log_it(L_ERROR, "Tracking value overflow, can't track voting %s anymore", dap_hash_fast_to_str_static(&l_new_tracker->voting_hash));
-            return a_trackers;
-        }
-        l_exists_tracker->cur_value = l_exists_tracker->colored_value;
-        if (!l_exists)
+            dap_strncpy(l_exists_tracker->ticker, a_ticker, DAP_CHAIN_TICKER_SIZE_MAX);
             a_trackers = dap_list_append(a_trackers, l_exists_tracker);
+        } else
+            l_exists_tracker = l_exists->data;
+        dap_ledger_tracker_item_t *l_item;
+        DL_FOREACH(l_new_tracker->items, l_item) {
+            if (a_voting_hash && dap_hash_fast_compare(a_voting_hash, &l_exists_tracker->voting_hash) &&
+                    dap_hash_fast_compare(a_pkey_hash, &l_item->pkey_hash))
+                continue;
+            struct tracker_mover_item *l_exists_item, l_sought = { .pkey_hash = l_item->pkey_hash };
+            DL_SEARCH(l_exists_tracker->items, l_exists_item, &l_sought, s_compare_tracker_items);
+            if (!l_exists_item) {
+                l_exists_item = DAP_NEW_Z_RET_VAL_IF_FAIL(struct tracker_mover_item, a_trackers);
+                l_exists_item->pkey_hash = l_item->pkey_hash;
+                DL_APPEND(l_exists_tracker->items, l_exists_item);
+            }
+            if (SUM_256_256(l_exists_item->coloured_value, l_item->coloured_value, &l_exists_item->coloured_value)) {
+                log_it(L_ERROR, "Tracking value overflow, can't track voting %s anymore", dap_hash_fast_to_str_static(&l_new_tracker->voting_hash));
+                return a_trackers;
+            }
+            l_exists_item->cur_value = l_exists_item->coloured_value;
+        }
     }
     return a_trackers;
 }
@@ -1245,10 +1286,11 @@ int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ha
     int l_ret_check;
     dap_chain_srv_uid_t l_tag =  { .uint64 = 0 };
     dap_chain_tx_tag_action_type_t l_action = DAP_CHAIN_TX_TAG_ACTION_UNKNOWN;
-
+    dap_ledger_tokenizer_t *l_values_from_cur_tx = NULL;
     if( (l_ret_check = s_tx_cache_check(a_ledger, a_tx, &l_tx_hash, a_from_threshold,
-                                                       &l_list_bound_items, &l_list_tx_out,
-                                                       l_main_token_ticker, &l_tag, &l_action, false))) {
+                                        &l_list_bound_items, &l_list_tx_out,
+                                        l_main_token_ticker, &l_values_from_cur_tx,
+                                        &l_tag, &l_action, false))) {
         if ((l_ret_check == DAP_CHAIN_CS_VERIFY_CODE_TX_NO_PREVIOUS ||
                 l_ret_check == DAP_CHAIN_CS_VERIFY_CODE_TX_NO_EMISSION) &&
                 is_ledger_threshld(l_ledger_pvt) && !dap_chain_net_get_load_mode(a_ledger->net)) {
@@ -1287,11 +1329,16 @@ int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ha
     }
 
     int l_err_num = 0;
+    dap_hash_fast_t l_vote_pkey_hash = { };
+    dap_chain_tx_vote_t *l_vote_tx_item = NULL;
     if (s_voting_callbacks.voting_callback) {
         if (l_action == DAP_CHAIN_TX_TAG_ACTION_VOTING)
-            l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTING, a_tx, &l_tx_hash, true);
-        else if (l_action == DAP_CHAIN_TX_TAG_ACTION_VOTE)
-            l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTE, a_tx, &l_tx_hash, true);
+            l_err_num = s_voting_callbacks.voting_callback(a_ledger, a_tx, &l_tx_hash, true);
+        else if (l_action == DAP_CHAIN_TX_TAG_ACTION_VOTE) {
+            l_err_num = s_voting_callbacks.vote_callback(a_ledger, a_tx, &l_tx_hash, &l_vote_pkey_hash, true);
+            l_vote_tx_item = (dap_chain_tx_vote_t *)dap_chain_datum_tx_item_get(a_tx, NULL, NULL, TX_ITEM_TYPE_VOTE, NULL);
+            assert(l_vote_tx_item);
+        }
     }
     assert(!l_err_num);
 
@@ -1389,10 +1436,13 @@ int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ha
         // Gather colour information from previous outputs
         dap_ledger_tx_item_t *l_prev_item_out = l_bound_item->prev_item;
         l_prev_item_out->out_metadata[l_bound_item->prev_out_idx].tx_spent_hash_fast = l_tx_hash;
-        l_trackers_mover = s_trackers_aggregate(a_ledger, l_trackers_mover,
-                                                &l_prev_item_out->out_metadata[l_bound_item->prev_out_idx].trackers, a_tx->header.ts_created);
-        if (l_prev_item_out->cache_data.flags & LEDGER_PVT_TX_META_FLAG_IMMUTABLE) // Clear trackers info for immutable tx's
-            dap_list_free_full(l_prev_item_out->out_metadata[l_bound_item->prev_out_idx].trackers, NULL);
+        // Clear trackers info for immutable tx's
+        if (l_prev_item_out->cache_data.flags & LEDGER_PVT_TX_META_FLAG_IMMUTABLE)
+            dap_list_free_full(l_prev_item_out->out_metadata[l_bound_item->prev_out_idx].trackers, dap_ledger_colour_clear_callback);
+        else
+            l_trackers_mover = s_trackers_aggregate(a_ledger, l_trackers_mover, l_bound_item->in.token_ticker,
+                                                    l_vote_tx_item ? &l_vote_tx_item->voting_hash : NULL, &l_vote_pkey_hash,
+                                                    &l_prev_item_out->out_metadata[l_bound_item->prev_out_idx].trackers, a_tx->header.ts_created);
         // add a used output
         l_prev_item_out->cache_data.n_outs_used++;
         if ( is_ledger_cached(l_ledger_pvt) ) {
@@ -1418,94 +1468,7 @@ int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ha
             l_prev_item_out->cache_data.ts_spent = a_tx->header.ts_created;
     }
 
-    //Update balance : raise
-    bool l_cross_network = false;
-    uint32_t l_outs_count = 0;
-    uint256_t l_native_value = {};
-    for (dap_list_t *l_tx_out = l_list_tx_out; l_tx_out; l_tx_out = l_tx_out->next, l_outs_count++) {
-        assert(l_tx_out->data);
-        dap_chain_tx_item_type_t l_type = *(uint8_t *)l_tx_out->data;
-        if (l_type == TX_ITEM_TYPE_OUT_COND) {
-            // Update service items if any
-            dap_chain_tx_out_cond_t *l_cond = (dap_chain_tx_out_cond_t *)l_tx_out->data;
-            dap_ledger_verificator_t *l_verificator = NULL;
-            int l_tmp = l_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_out_add)
-                l_verificator->callback_out_add(a_ledger, a_tx, &l_tx_hash, l_cond);
-            if (!dap_strcmp(l_main_token_ticker, a_ledger->net->pub.native_ticker))
-                SUM_256_256(l_native_value, l_cond->header.value, &l_native_value);
-            continue;   // balance raise will be with next conditional transaction
-        }
-
-        dap_chain_addr_t *l_addr = NULL;
-        uint256_t l_value = {};
-        switch (l_type) {
-        case TX_ITEM_TYPE_OUT: {
-            dap_chain_tx_out_t *l_out_item_256 = (dap_chain_tx_out_t *)l_tx_out->data;
-            l_addr = &l_out_item_256->addr;
-            l_value = l_out_item_256->header.value;
-            l_cur_token_ticker = l_main_token_ticker;
-        } break;
-        case TX_ITEM_TYPE_OUT_OLD: {
-            dap_chain_tx_out_old_t *l_out_item = (dap_chain_tx_out_old_t *)l_tx_out->data;
-            l_addr = &l_out_item->addr;
-            l_value = GET_256_FROM_64(l_out_item->header.value);
-            l_cur_token_ticker = l_main_token_ticker;
-        } break;
-        case TX_ITEM_TYPE_OUT_EXT: {
-            dap_chain_tx_out_ext_t *l_out_item_ext_256 = (dap_chain_tx_out_ext_t *)l_tx_out->data;
-            l_addr = &l_out_item_ext_256->addr;
-            l_value = l_out_item_ext_256->header.value;
-            l_cur_token_ticker = l_out_item_ext_256->token;
-        } break;
-        default:
-            log_it(L_ERROR, "Unknown item type %d", l_type);
-            break;
-        }
-        if (!dap_strcmp(a_ledger->net->pub.native_ticker, l_cur_token_ticker))
-            SUM_256_256(l_native_value, l_value, &l_native_value);
-        assert(l_addr);
-        if (l_addr->net_id.uint64 != a_ledger->net->pub.id.uint64 &&
-                !dap_chain_addr_is_blank(l_addr))
-            l_cross_network = true;
-        const char *l_addr_str = dap_chain_addr_to_str_static(l_addr);
-        dap_ledger_wallet_balance_t *wallet_balance = NULL;
-        char *l_wallet_balance_key = dap_strjoin(" ", l_addr_str, l_cur_token_ticker, (char*)NULL);
-        debug_if(g_debug_ledger, L_DEBUG, "GOT %s to addr: %s",
-            dap_uint256_to_char(l_value, NULL), l_wallet_balance_key);
-        pthread_rwlock_rdlock(&l_ledger_pvt->balance_accounts_rwlock);
-        HASH_FIND_STR(PVT(a_ledger)->balance_accounts, l_wallet_balance_key, wallet_balance);
-        pthread_rwlock_unlock(&l_ledger_pvt->balance_accounts_rwlock);
-        if (wallet_balance) {
-            SUM_256_256(wallet_balance->balance, l_value, &wallet_balance->balance);
-            DAP_DELETE (l_wallet_balance_key);
-            // Update the cache
-            s_balance_cache_update(a_ledger, wallet_balance);
-        } else {
-            wallet_balance = DAP_NEW_Z(dap_ledger_wallet_balance_t);
-            if (!wallet_balance) {
-                log_it(L_CRITICAL, "Memory allocation error in s_load_cache_gdb_loaded_txs_callback");
-                l_ret = -1;
-                goto FIN;
-            }
-            wallet_balance->key = l_wallet_balance_key;
-            strcpy(wallet_balance->token_ticker, l_cur_token_ticker);
-            SUM_256_256(wallet_balance->balance, l_value, &wallet_balance->balance);
-            if(g_debug_ledger)
-                log_it(L_DEBUG, "Create new balance item: %s %s", l_addr_str, l_cur_token_ticker);
-            pthread_rwlock_wrlock(&l_ledger_pvt->balance_accounts_rwlock);
-            HASH_ADD_KEYPTR(hh, PVT(a_ledger)->balance_accounts, wallet_balance->key,
-                            strlen(l_wallet_balance_key), wallet_balance);
-            pthread_rwlock_unlock(&l_ledger_pvt->balance_accounts_rwlock);
-            // Add it to cache
-            s_balance_cache_update(a_ledger, wallet_balance);
-        }
-    }
-
-    // add transaction to the cache list
+    uint32_t l_outs_count = dap_list_length(l_list_tx_out);
     dap_ledger_tx_item_t *l_tx_item = DAP_NEW_Z_SIZE(dap_ledger_tx_item_t, sizeof(dap_ledger_tx_item_t) + l_outs_count * sizeof(dap_ledger_tx_out_metadata_t));
     if ( !l_tx_item ) {
         log_it(L_CRITICAL, "%s", c_error_memory_alloc);
@@ -1520,85 +1483,165 @@ int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ha
     l_tx_item->cache_data.action = l_action;
     dap_strncpy(l_tx_item->cache_data.token_ticker, l_main_token_ticker, sizeof(l_tx_item->cache_data.token_ticker));
 
-    // Moving colour to new outputs
-    dap_chain_tx_vote_t *l_vote_tx_item;
-    if (l_action == DAP_CHAIN_TX_TAG_ACTION_VOTE) {
-        l_vote_tx_item = (dap_chain_tx_vote_t *)dap_chain_datum_tx_item_get(a_tx, NULL, NULL, TX_ITEM_TYPE_VOTE, NULL);
-        assert(l_vote_tx_item);
-    }
-    bool l_multichannel = false;
-    size_t i = 0;
-    for (dap_list_t *it = l_list_tx_out; it; it = it->next, i++) {
-        dap_chain_tx_item_type_t l_type = *(uint8_t *)it->data;
+    //Update balance : raise
+    bool l_cross_network = false, l_multichannel = false;
+    uint32_t i = 0;
+    for (dap_list_t *l_tx_out = l_list_tx_out; l_tx_out; l_tx_out = l_tx_out->next, i++) {
+        assert(l_tx_out->data);
+        dap_chain_tx_item_type_t l_type = *(uint8_t *)l_tx_out->data;
+
+        dap_chain_addr_t *l_addr = NULL;
         uint256_t l_value = {};
         switch (l_type) {
         case TX_ITEM_TYPE_OUT: {
-            dap_chain_tx_out_t *l_out_item_256 = (dap_chain_tx_out_t *)it->data;
+            dap_chain_tx_out_t *l_out_item_256 = (dap_chain_tx_out_t *)l_tx_out->data;
+            l_addr = &l_out_item_256->addr;
             l_value = l_out_item_256->header.value;
             l_cur_token_ticker = l_main_token_ticker;
         } break;
         case TX_ITEM_TYPE_OUT_OLD: {
-            dap_chain_tx_out_old_t *l_out_item = (dap_chain_tx_out_old_t *)it->data;
+            dap_chain_tx_out_old_t *l_out_item = (dap_chain_tx_out_old_t *)l_tx_out->data;
+            l_addr = &l_out_item->addr;
             l_value = GET_256_FROM_64(l_out_item->header.value);
             l_cur_token_ticker = l_main_token_ticker;
         } break;
         case TX_ITEM_TYPE_OUT_EXT: {
-            dap_chain_tx_out_ext_t *l_out_item_ext_256 = (dap_chain_tx_out_ext_t *)it->data;
+            dap_chain_tx_out_ext_t *l_out_item_ext_256 = (dap_chain_tx_out_ext_t *)l_tx_out->data;
+            l_addr = &l_out_item_ext_256->addr;
             l_value = l_out_item_ext_256->header.value;
             l_cur_token_ticker = l_out_item_ext_256->token;
         } break;
         case TX_ITEM_TYPE_OUT_COND: {
             // Update service items if any
-            dap_chain_tx_out_cond_t *l_cond = (dap_chain_tx_out_cond_t *)it->data;
-            l_value = l_cond->header.value;
-            l_cur_token_ticker = l_main_token_ticker;
+            dap_chain_tx_out_cond_t *l_cond = (dap_chain_tx_out_cond_t *)l_tx_out->data;
+            dap_ledger_verificator_t *l_verificator = NULL;
+            int l_tmp = l_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_out_add)
+                l_verificator->callback_out_add(a_ledger, a_tx, &l_tx_hash, l_cond);
         } break;
         default:
             log_it(L_ERROR, "Unknown item type %d", l_type);
-            break;
+            goto FIN;
         }
-        if (!l_multichannel && dap_strcmp(l_main_token_ticker, l_cur_token_ticker))
-            l_multichannel = true;
-        if (dap_strcmp(a_ledger->net->pub.native_ticker, l_cur_token_ticker))
-            continue;
-        if (l_action == DAP_CHAIN_TX_TAG_ACTION_VOTE) {
-            dap_ledger_tracker_t *l_new_tracker = DAP_NEW_Z(dap_ledger_tracker_t);
-            if (!l_new_tracker) {
-                log_it(L_CRITICAL, "%s", c_error_memory_alloc);
-                l_ret = DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY;
-                goto FIN;
+        // for conditional out balance isn't raised
+        if (l_type != TX_ITEM_TYPE_OUT_COND) {
+            assert(l_addr);
+            if (l_addr->net_id.uint64 != a_ledger->net->pub.id.uint64 &&
+                    !dap_chain_addr_is_blank(l_addr))
+                l_cross_network = true;
+            if (!l_multichannel && dap_strcmp(l_main_token_ticker, l_cur_token_ticker))
+                l_multichannel = true;
+            const char *l_addr_str = dap_chain_addr_to_str_static(l_addr);
+            dap_ledger_wallet_balance_t *wallet_balance = NULL;
+            char *l_wallet_balance_key = dap_strjoin(" ", l_addr_str, l_cur_token_ticker, (char*)NULL);
+            debug_if(g_debug_ledger, L_DEBUG, "GOT %s to addr: %s",
+                dap_uint256_to_char(l_value, NULL), l_wallet_balance_key);
+            pthread_rwlock_rdlock(&l_ledger_pvt->balance_accounts_rwlock);
+            HASH_FIND_STR(PVT(a_ledger)->balance_accounts, l_wallet_balance_key, wallet_balance);
+            pthread_rwlock_unlock(&l_ledger_pvt->balance_accounts_rwlock);
+            if (wallet_balance) {
+                SUM_256_256(wallet_balance->balance, l_value, &wallet_balance->balance);
+                DAP_DELETE (l_wallet_balance_key);
+                // Update the cache
+                s_balance_cache_update(a_ledger, wallet_balance);
+            } else {
+                wallet_balance = DAP_NEW_Z(dap_ledger_wallet_balance_t);
+                if (!wallet_balance) {
+                    log_it(L_CRITICAL, "Memory allocation error in s_load_cache_gdb_loaded_txs_callback");
+                    l_ret = -1;
+                    goto FIN;
+                }
+                wallet_balance->key = l_wallet_balance_key;
+                strcpy(wallet_balance->token_ticker, l_cur_token_ticker);
+                SUM_256_256(wallet_balance->balance, l_value, &wallet_balance->balance);
+                if(g_debug_ledger)
+                    log_it(L_DEBUG, "Create new balance item: %s %s", l_addr_str, l_cur_token_ticker);
+                pthread_rwlock_wrlock(&l_ledger_pvt->balance_accounts_rwlock);
+                HASH_ADD_KEYPTR(hh, PVT(a_ledger)->balance_accounts, wallet_balance->key,
+                                strlen(l_wallet_balance_key), wallet_balance);
+                pthread_rwlock_unlock(&l_ledger_pvt->balance_accounts_rwlock);
+                // Add it to cache
+                s_balance_cache_update(a_ledger, wallet_balance);
             }
-            l_new_tracker->voting_hash = l_vote_tx_item->voting_hash;
-            l_new_tracker->colored_value = l_value;
-            l_tx_item->out_metadata[i].trackers = dap_list_append(l_tx_item->out_metadata[i].trackers, l_new_tracker);
         }
+
+        // Moving colour to new outputs
+        bool l_voting_found = false;
         for (dap_list_t *mv = l_trackers_mover; mv; mv = mv->next) {
             struct tracker_mover *l_mover = mv->data;
-            if (IS_ZERO_256(l_mover->cur_value) || dap_hash_fast_is_blank(&l_mover->voting_hash)) {
-                log_it(L_ERROR, "Illegal tracker mover item detected");
+            assert(!dap_hash_fast_is_blank(&l_mover->voting_hash));
+            bool l_vote_add = false;
+            if (!l_voting_found && l_vote_tx_item && dap_hash_fast_compare(&l_mover->voting_hash, &l_vote_tx_item->voting_hash))
+                l_voting_found = l_vote_add = true;
+            if (!dap_strcmp(l_cur_token_ticker, l_mover->ticker))
                 continue;
+            dap_ledger_tokenizer_t *l_moving_sum = s_tokenizer_find(l_values_from_cur_tx, l_mover->ticker);
+            assert(l_moving_sum);
+            dap_ledger_tracker_t *l_tracker = NULL;
+            struct tracker_mover_item *it;
+            uint256_t l_moved_value = {};
+            DL_FOREACH(l_mover->items, it) {
+                if (IS_ZERO_256(it->cur_value))
+                    continue;
+                if (!l_tracker) {
+                    l_tracker = DAP_NEW_Z(dap_ledger_tracker_t);
+                    if (!l_tracker) {
+                        log_it(L_CRITICAL, "%s", c_error_memory_alloc);
+                        goto FIN;
+                    }
+                    l_tracker->voting_hash = l_mover->voting_hash;
+                }
+                dap_ledger_tracker_item_t *l_tracker_item = DAP_NEW_Z(dap_ledger_tracker_item_t);
+                if (!l_tracker_item) {
+                    log_it(L_CRITICAL, "%s", c_error_memory_alloc);
+                    goto FIN;
+                }
+                l_tracker_item->pkey_hash = it->pkey_hash;
+                DL_APPEND(l_tracker->items, l_tracker_item);
+                uint256_t l_coloured_value;
+                MULT_256_256(l_value, it->coloured_value, &l_coloured_value);
+                DIV_256(l_coloured_value, l_moving_sum->sum, &l_coloured_value);
+                if (compare256(it->cur_value, l_coloured_value) > 0 &&
+                        !IS_ZERO_256(l_coloured_value)) {
+                    SUBTRACT_256_256(it->cur_value, l_coloured_value, &it->cur_value);
+                    l_tracker_item->coloured_value = l_coloured_value;
+                } else {
+                    l_tracker_item->coloured_value = it->cur_value;
+                    it->cur_value = uint256_0;
+                }
+                if (l_vote_add)
+                    SUM_256_256(l_moved_value, l_tracker_item->coloured_value, &l_moved_value);
             }
-            if (l_action == DAP_CHAIN_TX_TAG_ACTION_VOTE &&
-                    dap_hash_fast_compare(&l_vote_tx_item->voting_hash, &l_mover->voting_hash))
-                continue;   // Don't move trackers for current vote voting
+            if (l_vote_add) {
+                dap_ledger_tracker_item_t *l_tracker_item = DAP_NEW_Z(dap_ledger_tracker_item_t);
+                if (!l_tracker_item) {
+                    log_it(L_CRITICAL, "%s", c_error_memory_alloc);
+                    goto FIN;
+                }
+                l_tracker_item->pkey_hash = l_vote_pkey_hash;
+                assert(compare256(l_value, l_moved_value) > 0);
+                SUBTRACT_256_256(l_value, l_moved_value, &l_tracker_item->coloured_value);
+                l_vote_add = false;
+            }
+            l_tx_item->out_metadata[i].trackers = dap_list_append(l_tx_item->out_metadata[i].trackers, l_tracker);
 
-            dap_ledger_tracker_t *l_tracker = DAP_NEW_Z(dap_ledger_tracker_t);
-            if (!l_tracker) {
+        }
+        if (!l_voting_found && l_vote_tx_item) {
+            dap_ledger_tracker_t *l_new_tracker = DAP_NEW_Z(dap_ledger_tracker_t);
+            dap_ledger_tracker_item_t *l_item_new = DAP_NEW_Z(dap_ledger_tracker_item_t);
+            if (!l_new_tracker || !l_item_new) {
                 log_it(L_CRITICAL, "%s", c_error_memory_alloc);
+                l_ret = DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY;
                 goto FIN;
             }
-            l_tracker->voting_hash = l_mover->voting_hash;
-            uint256_t l_colored_value;
-            MULT_256_256(l_value, l_mover->colored_value, &l_colored_value);
-            DIV_256(l_colored_value, l_native_value, &l_colored_value);
-            if (compare256(l_mover->cur_value, l_colored_value) == 1) {
-                SUBTRACT_256_256(l_mover->cur_value, l_colored_value, &l_mover->cur_value);
-                l_tracker->colored_value = l_colored_value;
-            } else {
-                l_tracker->colored_value = l_mover->cur_value;
-                l_mover->cur_value = uint256_0;
-            }
-            l_tx_item->out_metadata[i].trackers = dap_list_append(l_tx_item->out_metadata[i].trackers, l_tracker);
+            l_new_tracker->voting_hash = l_vote_tx_item->voting_hash;
+            l_item_new->pkey_hash = l_vote_pkey_hash;
+            l_item_new->coloured_value = l_value;
+            DL_APPEND(l_new_tracker->items, l_item_new);
+            l_tx_item->out_metadata[i].trackers = dap_list_append(l_tx_item->out_metadata[i].trackers, l_new_tracker);
         }
     }
 
@@ -1607,15 +1650,52 @@ int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ha
             if (*l_item != TX_ITEM_TYPE_TSD)
                 continue;
             dap_tsd_t *l_tsd = (dap_tsd_t *)((dap_chain_tx_tsd_t *)l_item)->tsd;
-            if (l_tsd->type != DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TRACKER)
-                continue;
+            dap_ledger_tracker_t *l_tracker_current = NULL;
+            switch (l_tsd->type) {
+            case DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_VOTING_HASH: {
+                if (l_tsd->size != sizeof(dap_hash_fast_t)) {
+                    log_it(L_WARNING, "Illegal harfork datum tx TSD VOTING_HASH size %u", l_tsd->size);
+                    break;
+                }
+                l_tracker_current = DAP_NEW_Z(dap_ledger_tracker_t);
+                if (!l_tracker_current) {
+                    log_it(L_CRITICAL, "%s", c_error_memory_alloc);
+                    break;
+                }
+                l_tracker_current->voting_hash = *(dap_hash_fast_t *)l_tsd->data;
+                l_tx_item->out_metadata[0].trackers = dap_list_append(l_tx_item->out_metadata[0].trackers, l_tracker_current);
+            } break;
+            case DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TRACKER: {
+                if (l_tsd->size != sizeof(dap_ledger_hardfork_tracker_t)) {
+                    log_it(L_WARNING, "Illegal harfork datum tx TSD VOTING_HASH size %u", l_tsd->size);
+                    break;
+                }
+                if (!l_tracker_current) {
+                    log_it(L_WARNING, "No voting hash defined for tracking item");
+                    break;
+                }
+                dap_ledger_hardfork_tracker_t *l_tsd_item = (dap_ledger_hardfork_tracker_t *)l_tsd->data;
+                dap_ledger_tracker_item_t *l_item = DAP_NEW_Z(dap_ledger_tracker_item_t);
+                if (!l_tracker_current) {
+                    log_it(L_CRITICAL, "%s", c_error_memory_alloc);
+                    break;
+                }
+                l_item->pkey_hash = l_tsd_item->pkey_hash;
+                l_item->coloured_value = l_tsd_item->coloured_value;
+                DL_APPEND(l_tracker_current->items, l_item);
+            } break;
+            default:
+                log_it(L_WARNING, "Illegal harfork datum tx TSD item type 0x%X", l_tsd->type);
+                break;
+            }
             if (l_tsd->size != sizeof(dap_ledger_tracker_t)) {
                 log_it(L_WARNING, "Incorrect size of TSD tracker section %u (need %zu)", l_tsd->size, sizeof(dap_ledger_tracker_t));
                 break;
             }
-            l_tx_item->out_metadata[0].trackers = dap_list_append(l_tx_item->out_metadata[0].trackers, l_tsd->data);
         }
     }
+
+    // add transaction to the cache list
     if (l_multichannel)
         l_tx_item->cache_data.flags |= LEDGER_PVT_TX_META_FLAG_MULTICHANNEL;
     l_tx_item->ts_added = dap_nanotime_now();
@@ -1662,11 +1742,16 @@ int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ha
         dap_ledger_pvt_threshold_txs_proc(a_ledger);
 FIN:
     if (l_trackers_mover)
-        dap_list_free_full(l_trackers_mover, NULL);
+        dap_list_free_full(l_trackers_mover, dap_ledger_colour_clear_callback);
     if (l_list_bound_items)
         dap_list_free_full(l_list_bound_items, NULL);
     if (l_list_tx_out)
         dap_list_free(l_list_tx_out);
+    if (l_values_from_cur_tx) {
+        dap_ledger_tokenizer_t *it, *tmp;
+        LL_FOREACH_SAFE(l_values_from_cur_tx, it, tmp)
+            DAP_DELETE(it);
+    }
     if ( is_ledger_cached(l_ledger_pvt) ) {
         if (l_cache_used_outs) {
             for (size_t i = 1; i < l_outs_used; ++i) {
@@ -1704,7 +1789,7 @@ int dap_ledger_tx_remove(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap
     int l_ret_check;
     if( (l_ret_check = s_tx_cache_check(a_ledger, a_tx, a_tx_hash, false,
                                                        &l_list_bound_items, &l_list_tx_out,
-                                                       l_main_token_ticker, &l_tag, &l_action, true))) {
+                                                       l_main_token_ticker, NULL, &l_tag, &l_action, true))) {
         debug_if(g_debug_ledger, L_WARNING, "dap_ledger_tx_remove() tx %s not passed the check: %s ", l_tx_hash_str,
                     dap_ledger_check_error_str(l_ret_check));
         return l_ret_check;
@@ -1944,7 +2029,7 @@ int dap_ledger_tx_remove(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap
 
     // Clear & destroy item
     for (uint32_t i = 0; i < l_tx_item->cache_data.n_outs; i++)
-        dap_list_free_full(l_tx_item->out_metadata[i].trackers, NULL);
+        dap_list_free_full(l_tx_item->out_metadata[i].trackers, dap_ledger_colour_clear_callback);
     DAP_DELETE(l_tx_item);
 
     if ( is_ledger_cached(l_ledger_pvt) ) {
@@ -2050,8 +2135,8 @@ static dap_ledger_stake_lock_item_t *s_emissions_for_stake_lock_item_find(dap_le
  * a_public_key_size[in] public key size
  * a_tx_first_hash [in/out] hash of the initial transaction/ found transaction, if 0 start from the beginning
  */
-static dap_ledger_tx_item_t *s_tx_item_find_by_addr(dap_ledger_t *a_ledger, const dap_chain_addr_t *a_addr,
-                                                        const char * a_token, dap_chain_hash_fast_t *a_tx_first_hash)
+dap_chain_datum_tx_t* dap_ledger_tx_find_by_addr(dap_ledger_t *a_ledger , const char * a_token ,
+        const dap_chain_addr_t *a_addr, dap_chain_hash_fast_t *a_tx_first_hash)
 {
     if(!a_addr || !a_tx_first_hash)
         return NULL;
@@ -2108,20 +2193,7 @@ static dap_ledger_tx_item_t *s_tx_item_find_by_addr(dap_ledger_t *a_ledger, cons
             break;
     }
     pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock);
-    return is_tx_found ? l_iter_current : NULL;
-}
-
-/**
- * @brief dap_ledger_tx_find_by_addr
- * @param a_addr
- * @param a_tx_first_hash
- * @return
- */
- dap_chain_datum_tx_t* dap_ledger_tx_find_by_addr(dap_ledger_t *a_ledger , const char * a_token ,
-         const dap_chain_addr_t *a_addr, dap_chain_hash_fast_t *a_tx_first_hash)
-{
-    dap_ledger_tx_item_t* l_tx_item = s_tx_item_find_by_addr(a_ledger, a_addr, a_token, a_tx_first_hash);
-    return (l_tx_item) ? l_tx_item->tx : NULL;
+    return is_tx_found ? l_iter_current->tx : NULL;
 }
 
 /**
@@ -2257,7 +2329,9 @@ dap_list_t *dap_ledger_tx_get_trackers(dap_ledger_t *a_ledger, dap_chain_hash_fa
     return l_item_out->out_metadata[a_out_idx].trackers;
 }
 
-uint256_t dap_ledger_coin_get_uncoloured_value(dap_ledger_t *a_ledger, dap_hash_fast_t *a_voting_hash, dap_hash_fast_t *a_tx_hash, int a_out_idx)
+uint256_t dap_ledger_coin_get_uncoloured_value(dap_ledger_t *a_ledger, dap_hash_fast_t *a_voting_hash,
+                                               dap_hash_fast_t *a_tx_hash, int a_out_idx,
+                                               dap_hash_fast_t *a_pkey_hash)
 {
     dap_return_val_if_fail(a_ledger && a_voting_hash && a_tx_hash, uint256_0);
     dap_ledger_tx_item_t *l_item_out = NULL;
@@ -2288,14 +2362,28 @@ uint256_t dap_ledger_coin_get_uncoloured_value(dap_ledger_t *a_ledger, dap_hash_
     for (dap_list_t *it = l_item_out->out_metadata[a_out_idx].trackers; it ; it = it->next) {
         dap_ledger_tracker_t *l_tracker = it->data;
         if (dap_hash_fast_compare(&l_tracker->voting_hash, a_voting_hash)) {
-            assert(compare256(l_value, l_tracker->colored_value) >= 0);
-            SUBTRACT_256_256(l_value, l_tracker->colored_value, &l_value);
+            uint256_t l_coloured_value = {};
+            dap_ledger_tracker_item_t *l_item;
+            DL_FOREACH(l_tracker->items, l_item)
+                if (!a_pkey_hash || !dap_hash_fast_compare(a_pkey_hash, &l_item->pkey_hash))
+                    SUM_256_256(l_coloured_value, l_item->coloured_value, &l_coloured_value);
+            assert(compare256(l_value, l_coloured_value) >= 0);
+            SUBTRACT_256_256(l_value, l_coloured_value, &l_value);
             break;
         }
     }
     return l_value;
 }
 
+void dap_ledger_colour_clear_callback(void *a_list_data)
+{
+    dap_ledger_tracker_t *l_free = a_list_data;
+    dap_ledger_tracker_item_t *it, *tmp;
+    DL_FOREACH_SAFE(l_free->items, it, tmp)
+        DAP_DELETE(it); // No need for DL_DELETE cause clear the full list
+    DAP_DELETE(a_list_data);
+}
+
 void dap_ledger_tx_clear_colour(dap_ledger_t *a_ledger, dap_hash_fast_t *a_tx_hash)
 {
     dap_return_if_fail(a_ledger && a_tx_hash);
@@ -2308,7 +2396,7 @@ void dap_ledger_tx_clear_colour(dap_ledger_t *a_ledger, dap_hash_fast_t *a_tx_ha
     l_item_out->cache_data.flags |= LEDGER_PVT_TX_META_FLAG_IMMUTABLE;
     for (uint32_t i = 0; i < l_item_out->cache_data.n_outs; i++) {
         if (!dap_hash_fast_is_blank(&l_item_out->out_metadata[i].tx_spent_hash_fast)) {
-            dap_list_free_full(l_item_out->out_metadata[i].trackers, NULL);
+            dap_list_free_full(l_item_out->out_metadata[i].trackers, dap_ledger_colour_clear_callback);
             l_item_out->out_metadata[i].trackers = NULL;
         }
     }
@@ -2334,7 +2422,7 @@ dap_chain_token_ticker_str_t dap_ledger_tx_calculate_main_ticker_(dap_ledger_t *
 {
     dap_hash_fast_t l_tx_hash = dap_chain_node_datum_tx_calc_hash(a_tx);
     dap_chain_token_ticker_str_t l_ret = { };
-    int l_rc = s_tx_cache_check(a_ledger, a_tx, &l_tx_hash, false, NULL, NULL, (char*)&l_ret, NULL, NULL, false);
+    int l_rc = s_tx_cache_check(a_ledger, a_tx, &l_tx_hash, false, NULL, NULL, (char*)&l_ret, NULL, NULL, NULL, false);
     if (l_rc == DAP_LEDGER_CHECK_ALREADY_CACHED)
         dap_strncpy( (char*)&l_ret, dap_ledger_tx_get_token_ticker_by_hash(a_ledger, &l_tx_hash), DAP_CHAIN_TICKER_SIZE_MAX );
     if (a_ledger_rc)
@@ -2369,11 +2457,14 @@ int dap_ledger_verificator_add(dap_chain_tx_out_cond_subtype_t a_subtype,
     return 0;
 }
 
-int dap_ledger_voting_verificator_add(dap_ledger_voting_callback_t a_callback, dap_ledger_voting_delete_callback_t a_callback_delete, dap_ledger_voting_expire_callback_t a_callback_expire)
+int dap_ledger_voting_verificator_add(dap_ledger_voting_callback_t a_voting_callback, dap_ledger_vote_callback_t a_vote_callback,
+                                      dap_ledger_voting_delete_callback_t a_callback_delete, dap_ledger_voting_expire_callback_t a_callback_expire)
 {
-    dap_return_val_if_fail(a_callback && a_callback_delete && a_callback_expire, -1);
-    int ret = s_voting_callbacks.voting_callback || s_voting_callbacks.voting_delete_callback || s_voting_callbacks.voting_expire_callback ? 1 : 0;
-    s_voting_callbacks.voting_callback = a_callback;
+    dap_return_val_if_fail(a_voting_callback && a_vote_callback && a_callback_delete && a_callback_expire, -1);
+    int ret = s_voting_callbacks.voting_callback || s_voting_callbacks.vote_callback ||
+                s_voting_callbacks.voting_delete_callback || s_voting_callbacks.voting_expire_callback ? 1 : 0;
+    s_voting_callbacks.voting_callback = a_voting_callback;
+    s_voting_callbacks.vote_callback = a_vote_callback;
     s_voting_callbacks.voting_delete_callback = a_callback_delete;
     s_voting_callbacks.voting_expire_callback = a_callback_expire;
     return ret;
@@ -2450,6 +2541,10 @@ static int s_compare_balances(dap_ledger_hardfork_balances_t *a_list1, dap_ledge
     return ret ? ret : memcmp(a_list1->ticker, a_list2->ticker, DAP_CHAIN_TICKER_SIZE_MAX);
 }
 
+int s_compare_tracker_items_hardfork(dap_ledger_tracker_item_t *a_item1, dap_ledger_tracker_item_t *a_item2)
+{
+    return memcmp(&a_item1->pkey_hash, &a_item1->pkey_hash, sizeof(dap_hash_fast_t));
+}
 
 int s_compare_trackers_hardfork(dap_list_t *a_tracker1, dap_list_t *a_tracker2)
 {
@@ -2461,21 +2556,31 @@ dap_list_t *s_trackers_aggregate_hardfork(dap_ledger_t *a_ledger, dap_list_t *a_
 {
     dap_return_val_if_fail(s_voting_callbacks.voting_expire_callback, a_trackers);
     for (dap_list_t *it = a_added; it; it = it->next) {
-        dap_ledger_tracker_t *l_new_tracker = it->data;
+        dap_ledger_tracker_t *l_new_tracker = it->data, *l_exists_tracker = NULL;
+        dap_time_t l_exp_time = s_voting_callbacks.voting_expire_callback(a_ledger, &l_new_tracker->voting_hash);
+        if (a_ts_creation_time > l_exp_time)    // Don't track expired votings
+            continue;
         dap_list_t *l_exists = dap_list_find(a_trackers, &l_new_tracker->voting_hash, s_compare_trackers_hardfork);
-        dap_ledger_tracker_t *l_exists_tracker = NULL;
-        if (l_exists)
-            l_exists_tracker = l_exists->data;
-        else {
+        if (!l_exists) {
             l_exists_tracker = DAP_NEW_Z_RET_VAL_IF_FAIL(dap_ledger_tracker_t, a_trackers);
             l_exists_tracker->voting_hash = l_new_tracker->voting_hash;
-        }
-        if (SUM_256_256(l_exists_tracker->colored_value, l_new_tracker->colored_value, &l_exists_tracker->colored_value)) {
-            log_it(L_ERROR, "Tracking value overflow, can't track voting %s anymore", dap_hash_fast_to_str_static(&l_new_tracker->voting_hash));
-            return a_trackers;
-        }
-        if (!l_exists)
             a_trackers = dap_list_append(a_trackers, l_exists_tracker);
+        } else
+            l_exists_tracker = l_exists->data;
+        dap_ledger_tracker_item_t *l_item, *l_exists_item;
+        DL_FOREACH(l_new_tracker->items, l_item) {
+            dap_ledger_tracker_item_t l_sought = { .pkey_hash = l_item->pkey_hash };
+            DL_SEARCH(l_exists_tracker->items, l_exists_item, &l_sought, s_compare_tracker_items_hardfork);
+            if (!l_exists_item) {
+                l_exists_item = DAP_NEW_Z_RET_VAL_IF_FAIL(dap_ledger_tracker_item_t, a_trackers);
+                l_exists_item->pkey_hash = l_item->pkey_hash;
+                DL_APPEND(l_exists_tracker->items, l_exists_item);
+            }
+            if (SUM_256_256(l_exists_item->coloured_value, l_item->coloured_value, &l_exists_item->coloured_value)) {
+                log_it(L_ERROR, "Tracking value overflow, can't track voting %s for hardfork", dap_hash_fast_to_str_static(&l_new_tracker->voting_hash));
+                return a_trackers;
+            }
+        }
     }
     return a_trackers;
 }
diff --git a/modules/ledger/include/dap_chain_ledger.h b/modules/ledger/include/dap_chain_ledger.h
index 2de3e3060d..e131ee8748 100644
--- a/modules/ledger/include/dap_chain_ledger.h
+++ b/modules/ledger/include/dap_chain_ledger.h
@@ -47,10 +47,21 @@ typedef struct dap_ledger {
     void *_internal;
 } dap_ledger_t;
 
+typedef struct dap_ledger_tracker_item {
+    dap_hash_fast_t pkey_hash;
+    uint256_t coloured_value;
+    struct dap_ledger_tracker_item *prev, *next;
+} dap_ledger_tracker_item_t;
+
 typedef struct dap_ledger_tracker {
     dap_hash_fast_t voting_hash;
-    uint256_t colored_value;
-} DAP_ALIGN_PACKED dap_ledger_tracker_t;
+    dap_ledger_tracker_item_t *items;
+} dap_ledger_tracker_t;
+
+typedef struct dap_ledger_hardfork_tracker {
+    dap_hash_fast_t pkey_hash;
+    uint256_t coloured_value;
+} DAP_ALIGN_PACKED dap_ledger_hardfork_tracker_t;
 
 typedef struct dap_ledger_hardfork_balances {
     dap_chain_addr_t addr;
@@ -290,7 +301,8 @@ typedef void (*dap_ledger_cond_out_delete_callback_t)(dap_ledger_t *a_ledger, da
 typedef void (* dap_ledger_tx_add_notify_t)(void *a_arg, dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ledger_notify_opcodes_t a_opcode);
 typedef void (* dap_ledger_bridged_tx_notify_t)(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash, void *a_arg, dap_ledger_notify_opcodes_t a_opcode);
 typedef bool (*dap_ledger_cache_tx_check_callback_t)(dap_ledger_t *a_ledger, dap_hash_fast_t *a_tx_hash);
-typedef int (*dap_ledger_voting_callback_t)(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash, bool a_apply);
+typedef int (*dap_ledger_voting_callback_t)(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash, bool a_apply);
+typedef int (*dap_ledger_vote_callback_t)(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash, dap_hash_fast_t *a_pkey_hash, bool a_apply);
 typedef bool (*dap_ledger_voting_delete_callback_t)(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash);
 typedef dap_time_t (*dap_ledger_voting_expire_callback_t)(dap_ledger_t *a_ledger, dap_hash_fast_t *a_voting_hash);
 typedef bool (*dap_ledger_tag_check_callback_t)(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_chain_datum_tx_item_groups_t *a_items_grp, dap_chain_tx_tag_action_type_t *a_action);
@@ -445,38 +457,29 @@ dap_chain_datum_tx_t* dap_ledger_tx_find_by_addr(dap_ledger_t *a_ledger, const c
 
 bool dap_ledger_tx_check_recipient(dap_ledger_t* a_ledger, dap_chain_hash_fast_t* a_tx_prev_hash, dap_chain_addr_t *a_addr);
 
-// Get the transaction in the cache by the public key that signed the transaction, starting with a_tx_first_hash
-const dap_chain_datum_tx_t* dap_ledger_tx_find_by_pkey(dap_ledger_t *a_ledger,
-        char *a_public_key, size_t a_public_key_size, dap_chain_hash_fast_t *a_tx_first_hash);
-
-// Get the transaction in the cache with the out_cond item
-dap_chain_datum_tx_t* dap_ledger_tx_cache_find_out_cond(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_cond_type,
-                                                              dap_chain_hash_fast_t *a_tx_first_hash, dap_chain_tx_out_cond_t **a_out_cond,
-                                                              int *a_out_cond_idx, char *a_token_ticker);
-
 // Get all transactions from the cache with the specified out_cond items
 dap_list_t* dap_ledger_tx_cache_find_out_cond_all(dap_ledger_t *a_ledger, dap_chain_srv_uid_t a_srv_uid);
 
-// Get the value from all transactions in the cache with out_cond item
-uint256_t dap_ledger_tx_cache_get_out_cond_value(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_cond_type, dap_chain_addr_t *a_addr,
-                                                       dap_chain_tx_out_cond_t **tx_out_cond);
 // Get first tx with type a_subtype
 dap_chain_tx_used_out_item_t *dap_ledger_get_tx_cond_out(dap_ledger_t *a_ledger, const dap_chain_addr_t *a_addr_from, dap_chain_tx_out_cond_subtype_t a_subtype);
 
+dap_chain_tx_out_cond_t *dap_ledger_out_cond_unspent_find_by_addr(dap_ledger_t *a_ledger, const char *a_token, dap_chain_tx_out_cond_subtype_t a_subtype,
+                                                                  const dap_chain_addr_t *a_addr, dap_chain_hash_fast_t *a_tx_first_hash, int *a_out_idx);
+
 dap_list_t *dap_ledger_get_list_tx_outs(dap_ledger_t *a_ledger, const char *a_token_ticker, const dap_chain_addr_t *a_addr_from,
                                         uint256_t *a_value_transfer);
-// Get the list of 'out_cond' items with summary value >= than a_value_need
-dap_list_t *dap_ledger_get_list_tx_cond_outs_with_val(dap_ledger_t *a_ledger, const char *a_token_ticker,  const dap_chain_addr_t *a_addr_from,
-        dap_chain_tx_out_cond_subtype_t a_subtype, uint256_t a_value_need, uint256_t *a_value_transfer);
 
-dap_list_t *dap_ledger_get_list_tx_cond_outs(dap_ledger_t *a_ledger,  const dap_chain_addr_t *a_addr_from);
+dap_list_t *dap_ledger_get_list_tx_cond_outs(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_subtype, const char *a_token_ticker,  const dap_chain_addr_t *a_addr_from);
+bool dap_ledger_check_condition_owner(dap_ledger_t *a_ledger, dap_hash_fast_t *a_tx_hash, dap_chain_tx_out_cond_subtype_t a_cond_subtype, int a_out_idx, dap_sign_t *a_owner_sign);
+
 // Add new verificator callback with associated subtype. Returns 1 if callback replaced, overwise returns 0
 int dap_ledger_verificator_add(dap_chain_tx_out_cond_subtype_t a_subtype,
                                dap_ledger_cond_in_verify_callback_t a_callback_in_verify, dap_ledger_cond_out_verify_callback_t a_callback_out_verify,
                                dap_ledger_cond_in_add_callback_t a_callback_in_add, dap_ledger_cond_out_add_callback_t a_callback_out_add,
                                dap_ledger_cond_in_delete_callback_t a_callback_in_delete, dap_ledger_cond_out_delete_callback_t a_callback_out_delete);
 // Add new verificator callback for voting. Returns 1 if callback replaced, overwise returns 0
-int dap_ledger_voting_verificator_add(dap_ledger_voting_callback_t a_callback, dap_ledger_voting_delete_callback_t a_callback_delete, dap_ledger_voting_expire_callback_t a_callback_expire);
+int dap_ledger_voting_verificator_add(dap_ledger_voting_callback_t a_voting_callback, dap_ledger_vote_callback_t a_vote_callback,
+                                      dap_ledger_voting_delete_callback_t a_callback_delete, dap_ledger_voting_expire_callback_t a_callback_expire);
 int dap_ledger_tax_callback_set(dap_ledger_tax_callback_t a_callback);
 // Getting a list of transactions from the ledger.
 dap_list_t * dap_ledger_get_txs(dap_ledger_t *a_ledger, size_t a_count, size_t a_page, bool a_reverse, bool a_unspent_only);
@@ -521,9 +524,12 @@ int dap_ledger_anchor_purge(dap_ledger_t *a_ledger, dap_chain_id_t a_chain_id);
 dap_ledger_hardfork_balances_t *dap_ledger_states_aggregate(dap_ledger_t *a_ledger, dap_time_t a_hardfork_decree_creation_time, dap_ledger_hardfork_condouts_t **l_cond_outs_list, json_object* a_changed_addrs);
 dap_ledger_hardfork_anchors_t *dap_ledger_anchors_aggregate(dap_ledger_t *a_ledger, dap_chain_id_t a_chain_id);
 
-uint256_t dap_ledger_coin_get_uncoloured_value(dap_ledger_t *a_ledger, dap_hash_fast_t *a_voting_hash, dap_hash_fast_t *a_tx_hash, int a_out_idx);
+uint256_t dap_ledger_coin_get_uncoloured_value(dap_ledger_t *a_ledger, dap_hash_fast_t *a_voting_hash,
+                                               dap_hash_fast_t *a_tx_hash, int a_out_idx, dap_hash_fast_t *a_pkey_hash);
 dap_list_t *dap_ledger_tx_get_trackers(dap_ledger_t *a_ledger, dap_chain_hash_fast_t *a_tx_hash, uint32_t a_out_idx);
 void dap_ledger_tx_clear_colour(dap_ledger_t *a_ledger, dap_hash_fast_t *a_tx_hash);
+void dap_ledger_colour_clear_callback(void *a_list_data);
+
 dap_pkey_t *dap_ledger_find_pkey_by_hash(dap_ledger_t *a_ledger, dap_chain_hash_fast_t *a_pkey_hash);
 dap_list_t *dap_ledger_decrees_get_by_type(dap_ledger_t *a_ledger, int a_type);
 
diff --git a/modules/net/dap_chain_net_tx.c b/modules/net/dap_chain_net_tx.c
index 7561efa260..550083ddee 100644
--- a/modules/net/dap_chain_net_tx.c
+++ b/modules/net/dap_chain_net_tx.c
@@ -1657,7 +1657,7 @@ int dap_chain_net_tx_to_json(dap_chain_datum_tx_t *a_tx, json_object *a_out_json
             json_object_object_add(json_obj_item,"type", json_object_new_string("voting"));
             json_object_object_add(json_obj_item,"voting_question", json_object_new_string(l_voting_params->question));
             json_object_object_add(json_obj_item,"answer_options", json_object_new_string(""));
-
+            json_object_object_add(json_obj_item, "token", json_object_new_string(l_voting_params->token_ticker));
             dap_list_t *l_temp = l_voting_params->options;
             uint8_t l_index = 0;
             while (l_temp) {
@@ -1672,12 +1672,10 @@ int dap_chain_net_tx_to_json(dap_chain_datum_tx_t *a_tx, json_object *a_out_json
             if (l_voting_params->votes_max_count) {
                 json_object_object_add(json_obj_item, "votes_max_count", json_object_new_uint64(l_voting_params->votes_max_count));
             }
-            json_object_object_add(json_obj_item,"changing_vote_is", l_voting_params->vote_changing_allowed ? json_object_new_string("available") :
-                                    json_object_new_string("not available"));
-            l_voting_params->delegate_key_required ?
-                json_object_object_add(json_obj_item,"delegated_key_for_participating_in_voting", json_object_new_string("required")):
-                json_object_object_add(json_obj_item,"delegated_key_for_participating_in_voting", json_object_new_string("not required"));
-
+            json_object_object_add(json_obj_item, "changing_vote_is",
+                                   json_object_new_string(l_voting_params->vote_changing_allowed ? "available" : "not available"));
+            json_object_object_add(json_obj_item, "delegated_key_for_participating_in_voting",
+                                   json_object_new_string(l_voting_params->delegate_key_required ? "required" : "not required"));
             dap_list_free_full(l_voting_params->options, NULL);
             DAP_DELETE(l_voting_params->question);
             DAP_DELETE(l_voting_params);
diff --git a/modules/net/dap_chain_node.c b/modules/net/dap_chain_node.c
index 079ff718e9..b05535667c 100644
--- a/modules/net/dap_chain_node.c
+++ b/modules/net/dap_chain_node.c
@@ -610,15 +610,27 @@ dap_chain_datum_t *s_cond_tx_create(dap_chain_tx_out_cond_t *a_cond, dap_chain_t
     }
     for (dap_list_t *it = a_trackers; it; it = it->next) {
         dap_ledger_tracker_t *l_tracker = it->data;
-        dap_chain_tx_tsd_t *l_tracker_tsd = dap_chain_datum_tx_item_tsd_create(l_tracker, DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TRACKER, sizeof(dap_ledger_tracker_t));
-        if (!l_tracker_tsd) {
+        dap_chain_tx_tsd_t *l_tracker_hash_tsd = dap_chain_datum_tx_item_tsd_create(&l_tracker->voting_hash, DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_VOTING_HASH, sizeof(dap_hash_fast_t));
+        if (!l_tracker_hash_tsd) {
             dap_chain_datum_tx_delete(l_tx);
             return NULL;
         }
-        if (dap_chain_datum_tx_add_item(&l_tx, l_tracker_tsd) != 1) {
+        if (dap_chain_datum_tx_add_item(&l_tx, l_tracker_hash_tsd) != 1) {
             dap_chain_datum_tx_delete(l_tx);
             return NULL;
         }
+        for (dap_ledger_tracker_item_t *l_item = l_tracker->items; l_item; l_item = l_item->next) {
+            dap_ledger_hardfork_tracker_t l_hardfork_tracker = { .pkey_hash = l_item->pkey_hash, .coloured_value = l_item->coloured_value };
+            dap_chain_tx_tsd_t *l_tracker_tsd = dap_chain_datum_tx_item_tsd_create(&l_hardfork_tracker, DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TRACKER, sizeof(dap_ledger_hardfork_tracker_t));
+            if (!l_tracker_tsd) {
+                dap_chain_datum_tx_delete(l_tx);
+                return NULL;
+            }
+            if (dap_chain_datum_tx_add_item(&l_tx, l_tracker_tsd) != 1) {
+                dap_chain_datum_tx_delete(l_tx);
+                return NULL;
+            }
+        }
     }
     dap_chain_datum_t *l_datum_tx = dap_chain_datum_create(DAP_CHAIN_DATUM_TX, l_tx, dap_chain_datum_tx_get_size(l_tx));
     dap_chain_datum_tx_delete(l_tx);
@@ -783,11 +795,19 @@ static int s_compare_trackers(dap_list_t *a_list1, dap_list_t *a_list2)
         dap_ledger_tracker_t *l_tracker1 = it1->data,
                              *l_tracker2 = it2->data;
         ret = memcmp(&l_tracker1->voting_hash, &l_tracker1->voting_hash, sizeof(dap_hash_fast_t));
-        if (ret)
-            break;
-        ret = compare256(l_tracker1->colored_value, l_tracker2->colored_value);
-        if (ret)
+        if (ret)            // hash mismatch
             break;
+        dap_ledger_tracker_item_t *it1 = l_tracker1->items, *it2 = l_tracker2->items;
+        for (; it1 && it2; it1 = it1->next, it2 = it2->next) {
+            ret = memcmp(&it1->pkey_hash, &it2->pkey_hash, sizeof(dap_hash_fast_t));
+            if (ret)        // pkey mismatch
+                break;
+            ret = compare256(it1->coloured_value, it2->coloured_value);
+            if (ret)        // value mismatch
+                break;
+        }
+        if (it1 || it2)     // count mismatch
+            return 1;
     }
     return ret;
 }
@@ -880,6 +900,14 @@ int s_hardfork_check(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_
         }
     } break;
 
+#define m_ret_clear(rc) {                                                               \
+        if (l_regular.trackers)                                                         \
+            dap_list_free_full(l_regular.trackers, dap_ledger_colour_clear_callback);   \
+        if (l_conitional.trackers)                                                      \
+            dap_list_free_full(l_conitional.trackers, dap_ledger_colour_clear_callback);\
+        return rc;                                                                      \
+    }
+
     case DAP_CHAIN_DATUM_TX: {
         dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t *)a_datum->data;
         if (!l_tx->header.ts_created /* TODO add || l_tx->header.ts_created other criteria */) {
@@ -890,6 +918,7 @@ int s_hardfork_check(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_
         // Parse datum
         dap_ledger_hardfork_balances_t l_regular = {};
         dap_ledger_hardfork_condouts_t l_conitional = {};
+        dap_ledger_tracker_t *l_tracker_current = NULL;
         bool l_out = false;
         byte_t *l_item; size_t l_size;
         TX_ITEM_ITER_TX(l_item, l_size, l_tx) {
@@ -897,7 +926,7 @@ int s_hardfork_check(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_
             case TX_ITEM_TYPE_OUT_EXT:
                 if (l_out || l_conitional.cond) {
                     log_it(L_WARNING, "Additional OUT_EXT item for harfork datum tx is forbidden");
-                    return -4;
+                    m_ret_clear(-4);
                 }
                 l_out = true;
                 dap_chain_tx_out_ext_t *l_out_ext = (dap_chain_tx_out_ext_t *)l_item;
@@ -908,19 +937,23 @@ int s_hardfork_check(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_
             case TX_ITEM_TYPE_OUT_COND:
                 if (l_out || l_conitional.cond) {
                     log_it(L_WARNING, "Additional OUT_COND item for harfork datum tx is forbidden");
-                    return -5;
+                    m_ret_clear(-5);
                 }
                 l_conitional.cond = (dap_chain_tx_out_cond_t *)l_item;
                 break;
             case TX_ITEM_TYPE_SIG:
                 if (l_conitional.sign) {
                     log_it(L_WARNING, "Additional SIG item for harfork datum tx is forbidden");
-                    return -6;
+                    m_ret_clear(-6);
                 }
                 l_conitional.sign = (dap_chain_tx_sig_t *)l_item;
                 break;
             case TX_ITEM_TYPE_TSD: {
                 dap_chain_tx_tsd_t *l_tx_tsd = (dap_chain_tx_tsd_t *)l_item;
+                if (l_tx_tsd->header.size < sizeof(dap_tsd_t)) {
+                    log_it(L_WARNING, "Illegal harfork datum tx TSD header size %" DAP_UINT64_FORMAT_U, l_tx_tsd->header.size);
+                    m_ret_clear(-8);
+                }
                 dap_tsd_t *l_tsd = (dap_tsd_t *)l_tx_tsd->tsd;
                 switch (l_tsd->type) {
                 case DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TX_HASH:
@@ -929,24 +962,45 @@ int s_hardfork_check(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_
                 case DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TICKER:
                     if (!l_tsd->size || l_tsd->size > DAP_CHAIN_TICKER_SIZE_MAX) {
                         log_it(L_WARNING, "Illegal harfork datum tx TSD TICKER size %u", l_tsd->size);
-                        return -8;
+                        m_ret_clear(-8);
                     }
                     dap_strncpy(l_conitional.ticker, (char *)l_tsd->data, DAP_CHAIN_TICKER_SIZE_MAX);
                     break;
-                case DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TRACKER:
+                case DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_VOTING_HASH: {
+                    if (l_tsd->size != sizeof(dap_hash_fast_t)) {
+                        log_it(L_WARNING, "Illegal harfork datum tx TSD VOTING_HASH size %u", l_tsd->size);
+                        m_ret_clear(-8);
+                    }
+                    l_tracker_current = DAP_NEW_Z_RET_VAL_IF_FAIL(dap_ledger_tracker_t, -2);
+                    l_tracker_current->voting_hash = *(dap_hash_fast_t *)l_tsd->data;
                     if (l_out)
-                        l_regular.trackers = dap_list_append(l_regular.trackers, l_tsd->data);
+                        l_regular.trackers = dap_list_append(l_regular.trackers, l_tracker_current);
                     else
-                        l_conitional.trackers = dap_list_append(l_conitional.trackers, l_tsd->data);
-                    break;
+                        l_conitional.trackers = dap_list_append(l_conitional.trackers, l_tracker_current);
+                } break;
+                case DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TRACKER: {
+                    if (l_tsd->size != sizeof(dap_ledger_hardfork_tracker_t)) {
+                        log_it(L_WARNING, "Illegal harfork datum tx TSD VOTING_HASH size %u", l_tsd->size);
+                        m_ret_clear(-8);
+                    }
+                    if (!l_tracker_current) {
+                        log_it(L_WARNING, "No voting hash defined for tracking item");
+                        m_ret_clear(-19);
+                    }
+                    dap_ledger_hardfork_tracker_t *l_tsd_item = (dap_ledger_hardfork_tracker_t *)l_tsd->data;
+                    dap_ledger_tracker_item_t *l_item = DAP_NEW_Z_RET_VAL_IF_FAIL(dap_ledger_tracker_item_t, -2);
+                    l_item->pkey_hash = l_tsd_item->pkey_hash;
+                    l_item->coloured_value = l_tsd_item->coloured_value;
+                    DL_APPEND(l_tracker_current->items, l_item);
+                } break;
                 default:
                     log_it(L_WARNING, "Illegal harfork datum tx TSD item type 0x%X", l_tsd->type);
-                    return -7;
+                    m_ret_clear(-7);
                 }
             } break;
             default:
                 log_it(L_WARNING, "Illegal harfork datum tx item type %d", *l_item);
-                return -4;
+                m_ret_clear(-4);
             }
         }
         // Call comparators
@@ -956,7 +1010,7 @@ int s_hardfork_check(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_
             if (l_found) {
                 if (a_remove) {
                     DL_DELETE(a_chain->hardfork_data->balances, l_found);
-                    dap_list_free_full(l_found->trackers, NULL);
+                    dap_list_free_full(l_found->trackers, dap_ledger_colour_clear_callback);
                     DAP_DELETE(l_found);
                     if (!a_chain->hardfork_data->balances)
                         a_chain->hardfork_data->state_current = STATE_CONDOUTS;
@@ -970,7 +1024,7 @@ int s_hardfork_check(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_
                 if (l_found) {
                     if (a_remove) {
                         DL_DELETE(a_chain->hardfork_data->condouts, l_found);
-                        dap_list_free_full(l_found->trackers, NULL);
+                        dap_list_free_full(l_found->trackers, dap_ledger_colour_clear_callback);
                         DAP_DELETE(l_found);
                         if (!a_chain->hardfork_data->condouts)
                             a_chain->hardfork_data->state_current = STATE_FEES;
@@ -994,14 +1048,15 @@ int s_hardfork_check(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_
             }
         } else {
             log_it(L_WARNING, "Illegal harfork datum tx item with no OUT");
-            return -8;
+            m_ret_clear(-18);
         }
         // Clean memory
         if (l_regular.trackers)
-            dap_list_free(l_regular.trackers);
+            dap_list_free_full(l_regular.trackers, dap_ledger_colour_clear_callback);
         if (l_conitional.trackers)
-            dap_list_free(l_conitional.trackers);
+            dap_list_free_full(l_conitional.trackers, dap_ledger_colour_clear_callback);
     } break;
+#undef m_ret_clear
 
     case DAP_CHAIN_DATUM_SERVICE_STATE: {
         dap_chain_srv_hardfork_state_t *l_found = NULL,
@@ -1078,6 +1133,7 @@ int s_hardfork_check(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_
         }
 
     } break;
+#undef m_ret
 
     default:
         log_it(L_WARNING, "Incorrect harfork datum type %u", a_datum->header.type_id);
diff --git a/modules/node-cli/dap_chain_node_cli_cmd_tx.c b/modules/node-cli/dap_chain_node_cli_cmd_tx.c
index 14a6733b94..ef3831c6fa 100644
--- a/modules/node-cli/dap_chain_node_cli_cmd_tx.c
+++ b/modules/node-cli/dap_chain_node_cli_cmd_tx.c
@@ -110,7 +110,7 @@ bool s_dap_chain_datum_tx_out_data(json_object* a_json_arr_reply,
     TX_ITEM_ITER_TX_TYPE(l_item, TX_ITEM_TYPE_OUT_ALL, l_size, i, a_datum) {
         ++l_out_idx;
         dap_hash_fast_t l_spender = { };
-        json_object *l_json_obj_out = NULL, *json_arr_colours = NULL;
+        json_object *l_json_obj_out = NULL, *l_json_arr_colours = NULL;
         if ( dap_ledger_tx_hash_is_used_out_item(a_ledger, a_tx_hash, l_out_idx, &l_spender) ) {
             char l_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE] = { '\0' };
             dap_hash_fast_to_str(&l_spender, l_hash_str, sizeof(l_hash_str));
@@ -125,18 +125,26 @@ bool s_dap_chain_datum_tx_out_data(json_object* a_json_arr_reply,
                 l_json_obj_out = json_object_new_object();
                 json_object_object_add(l_json_obj_out, "out_number", json_object_new_int(l_out_idx));
             }
-            json_arr_colours = json_object_new_array();
-            json_object_object_add(l_json_obj_out, "trackers", json_arr_colours);
+            l_json_arr_colours = json_object_new_array();
+            json_object_object_add(l_json_obj_out, "trackers", l_json_arr_colours);
         }
         for (dap_list_t *it = l_trackers; it; it = it->next) {
             dap_ledger_tracker_t *l_tracker = it->data;
             json_object *l_json_obj_tracker = json_object_new_object();
+            json_object_array_add(l_json_arr_colours, l_json_obj_tracker);
             const char *l_voling_hash_str = dap_hash_fast_to_str_static(&l_tracker->voting_hash);
             json_object_object_add(l_json_obj_tracker, "voting_hash", json_object_new_string(l_voling_hash_str));
-            const char *l_coloured_coins, *l_coloured_value = dap_uint256_to_char(l_tracker->colored_value, &l_coloured_coins);
-            json_object_object_add(l_json_obj_tracker, "coloured_coins", json_object_new_string(l_coloured_coins));
-            json_object_object_add(l_json_obj_tracker, "coloured_value", json_object_new_string(l_coloured_coins));
-            json_object_array_add(json_arr_colours, l_json_obj_tracker);
+            json_object *l_json_arr_tracker_items = json_object_new_array();
+            json_object_object_add(l_json_obj_tracker, "items", l_json_arr_tracker_items);
+            for (dap_ledger_tracker_item_t *l_item = l_tracker->items; l_item; l_item = l_item->next) {
+                json_object *l_json_obj_tracker_item = json_object_new_object();
+                json_object_array_add(l_json_arr_tracker_items, l_json_obj_tracker_item);
+                const char *l_pkey_hash_str = dap_hash_fast_to_str_static(&l_item->pkey_hash);
+                json_object_object_add(l_json_obj_tracker_item, "pkey_hash", json_object_new_string(l_pkey_hash_str));
+                const char *l_coloured_coins, *l_coloured_value = dap_uint256_to_char(l_item->coloured_value, &l_coloured_coins);
+                json_object_object_add(l_json_obj_tracker_item, "coloured_coins", json_object_new_string(l_coloured_coins));
+                json_object_object_add(l_json_obj_tracker_item, "coloured_value", json_object_new_string(l_coloured_value));
+            }
         }
         if (l_json_obj_out)
             json_object_array_add(json_arr_items, l_json_obj_out);
diff --git a/modules/service/stake/dap_chain_net_srv_stake_pos_delegate.c b/modules/service/stake/dap_chain_net_srv_stake_pos_delegate.c
index 835fbbe90f..1df12d34ac 100644
--- a/modules/service/stake/dap_chain_net_srv_stake_pos_delegate.c
+++ b/modules/service/stake/dap_chain_net_srv_stake_pos_delegate.c
@@ -3652,7 +3652,14 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, void **a_str_reply)
                     l_node_address_text_block = dap_strdup_printf("node_address:\t" NODE_ADDR_FP_STR,NODE_ADDR_FP_ARGS_S(l_tx_out_cond->subtype.srv_stake_pos_delegate.signer_node_addr));
                     json_object_object_add(l_json_obj_tx, "node_address", json_object_new_string(l_node_address_text_block));
                     json_object_object_add(l_json_obj_tx, "value_coins", json_object_new_string(l_coins));
-                    json_object_object_add(l_json_obj_tx, "value_datoshi", json_object_new_string(l_balance));
+                    json_object_object_add(l_json_obj_tx, "value_datoshi", json_object_new_string(l_balance));                   
+                    dap_hash_fast_t l_owner_hash = dap_ledger_get_first_chain_tx_hash(l_net->pub.ledger, DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE, &l_datum_hash);
+                    dap_chain_datum_tx_t *l_owner_tx = dap_hash_fast_is_blank(&l_owner_hash) ? l_datum_tx
+                                                                                             : dap_ledger_tx_find_by_hash(l_net->pub.ledger, &l_owner_hash);
+                    assert(l_owner_tx);
+                    dap_sign_t *l_owner_sign = dap_chain_datum_tx_get_sign(l_owner_tx, 0);
+                    dap_chain_addr_t l_owner_addr; dap_chain_addr_fill_from_sign(&l_owner_addr, l_owner_sign, l_net->pub.id);
+                    json_object_object_add(l_json_obj_tx, "owner_addr", json_object_new_string(dap_chain_addr_to_str_static(&l_owner_addr)));
                     json_object_array_add(l_json_arr_tx, l_json_obj_tx);
                     DAP_DELETE(l_node_address_text_block);
                 }
diff --git a/modules/service/voting/dap_chain_net_srv_voting.c b/modules/service/voting/dap_chain_net_srv_voting.c
index 3847e37142..bbd028763d 100644
--- a/modules/service/voting/dap_chain_net_srv_voting.c
+++ b/modules/service/voting/dap_chain_net_srv_voting.c
@@ -25,24 +25,15 @@
 #include "dap_common.h"
 #include "dap_chain_net_srv_voting.h"
 #include "dap_chain_datum_tx_voting.h"
-#include "dap_chain_datum_service_state.h"
 #include "dap_chain_net_srv_stake_pos_delegate.h"
 #include "dap_chain_net_tx.h"
 #include "dap_chain_mempool.h"
 #include "uthash.h"
 #include "dap_chain_srv.h"
 #include "dap_cli_server.h"
-#include "dap_chain_wallet_cache.h"
 
 #define LOG_TAG "dap_chain_net_srv_voting"
 
-
-struct voting_cond_outs {
-    dap_chain_hash_fast_t tx_hash;
-    int out_idx;
-    UT_hash_handle hh;
-};
-
 struct vote {
     dap_chain_hash_fast_t vote_hash;
     dap_chain_hash_fast_t pkey_hash;
@@ -67,8 +58,8 @@ static void s_callback_delete(void *a_service_internal);
 static int s_callback_purge(dap_chain_net_id_t a_net_id, void *a_service_internal);
 static byte_t *s_votings_backup(dap_chain_net_id_t a_net_id, uint64_t *a_state_size, uint32_t *a_state_count, void *a_service_internal);
 static int s_votings_restore(dap_chain_net_id_t a_net_id, byte_t *a_state, uint64_t a_state_size, uint32_t a_states_count);
-
-static int s_voting_ledger_verificator_callback(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash, bool a_apply);
+static int s_voting_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash, bool a_apply);
+static int s_vote_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash, dap_hash_fast_t *a_pkey_hash, bool a_apply);
 static bool s_datum_tx_voting_verification_delete_callback(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash);
 static int s_cli_voting(int argc, char **argv, void **a_str_reply);
 
@@ -91,10 +82,11 @@ static bool s_tag_check_voting(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_t
 
 int dap_chain_net_srv_voting_init()
 {
-    dap_ledger_voting_verificator_add(s_voting_ledger_verificator_callback, s_datum_tx_voting_verification_delete_callback, dap_chain_net_srv_voting_get_expiration_time);
+    dap_ledger_voting_verificator_add(s_voting_verificator, s_vote_verificator,
+                                      s_datum_tx_voting_verification_delete_callback, dap_chain_net_srv_voting_get_expiration_time);
     dap_cli_server_cmd_add("voting", s_cli_voting, "Voting commands.",
                             "voting create -net <net_name> -question <\"Question_string\"> -options <\"Option0\", \"Option1\" ... \"OptionN\"> [-expire <voting_expire_time_in_RCF822>] [-max_votes_count <Votes_count>] [-delegated_key_required] [-vote_changing_allowed] -fee <value> -w <fee_wallet_name>\n"
-                            "voting vote -net <net_name> -hash <voting_hash> -option_idx <option_index> [-cert <delegate_cert_name>] -fee <value> -w <fee_wallet_name>\n"
+                            "voting vote -net <net_name> -hash <voting_hash> -option_idx <option_index> [-cert <delegate_cert_name>] -fee <value> -w <fee_wallet_name> [-token <ticker>]\n"
                             "voting list -net <net_name>\n"
                             "voting dump -net <net_name> -hash <voting_hash>\n"
                             "Hint:\n"
@@ -242,7 +234,14 @@ static int s_voting_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_
         TX_ITEM_ITER_TX(l_item, l_tx_item_size, a_tx_in) {
             if (*l_item != TX_ITEM_TYPE_TSD)
                 continue;
-            dap_tsd_t *l_tsd = (dap_tsd_t *)((dap_chain_tx_tsd_t *)l_item)->tsd;
+            dap_chain_tx_tsd_t *l_tx_tsd = (dap_chain_tx_tsd_t *)l_item;
+            dap_tsd_t *l_tsd = (dap_tsd_t *)l_tx_tsd->tsd;
+            if (l_tx_tsd->header.size < sizeof(dap_tsd_t) ||
+                    l_tx_tsd->header.size != dap_tsd_size(l_tsd)) {
+                log_it(L_WARNING, "Incorrect size %" DAP_UINT64_FORMAT_U " of TX_TSD item for voting %s",
+                                                l_tx_tsd->header.size, dap_hash_fast_to_str_static(a_tx_hash));
+                return -DAP_LEDGER_CHECK_INVALID_SIZE;
+            }
             switch(l_tsd->type) {
             case VOTING_TSD_TYPE_QUESTION:
                 if (!l_tsd->size || *l_tsd->data == '\0') {
@@ -282,6 +281,11 @@ static int s_voting_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_
                     return -DAP_LEDGER_CHECK_INVALID_SIZE;
                 }
                 break;
+            case VOTING_TSD_TYPE_TOKEN:
+                if (!l_tsd->size || l_tsd->size >= DAP_CHAIN_TICKER_SIZE_MAX) {
+                    log_it(L_WARNING, "Incorrect size %u of TSD section TOKEN for voting %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash));
+                    return -DAP_LEDGER_CHECK_INVALID_SIZE;
+                }
             default:
                 break;
             }
@@ -298,15 +302,19 @@ static int s_voting_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_
     struct voting *l_item = DAP_NEW_Z_RET_VAL_IF_FAIL(struct voting, -DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY);
     l_item->hash = *a_tx_hash;
     l_item->start_time = a_tx_in->header.ts_created;
-    l_item->params = dap_chain_datum_tx_voting_parse_tsd(a_tx_in);
+    l_item->params = dap_chain_datum_tx_voting_parse_tsd(a_tx_in);   
     if (!l_item->params)
         return DAP_DELETE(l_item), -DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY;
+    if (!*l_item->params->token_ticker)
+        strcpy(l_item->params->token_ticker, a_ledger->net->pub.native_ticker);
     s_voting_add(a_ledger->net->pub.id, l_item);
 
+    log_it(L_NOTICE, "Voting with hash %s succefully added to ledger", dap_hash_fast_to_str_static(a_tx_hash));
+
     return DAP_LEDGER_CHECK_OK;
 }
 
-static int s_vote_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash, bool a_apply)
+static int s_vote_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash, dap_hash_fast_t *a_pkey_hash, bool a_apply)
 {
     dap_chain_tx_vote_t *l_vote_tx_item = (dap_chain_tx_vote_t *)dap_chain_datum_tx_item_get(a_tx_in, NULL, NULL, TX_ITEM_TYPE_VOTE, NULL);
     assert(l_vote_tx_item);
@@ -318,43 +326,43 @@ static int s_vote_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx
         return -5;
     }
 
-    dap_hash_fast_t pkey_hash = {};
-    int l_item_cnt = 0;
-    dap_list_t *l_signs_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_SIG, &l_item_cnt);
-
-    if (!l_signs_list) {
-        log_it(L_WARNING, "Can't get signs from tx %s", dap_chain_hash_fast_to_str_static(a_tx_hash));
-        return -9;
+    // Get last sign item from transaction
+    dap_hash_fast_t l_pkey_hash = {};
+    dap_sign_t *l_pkey_sign = NULL;
+    uint8_t *l_tx_item = NULL; size_t l_size; int i, l_sign_num = 0;
+    TX_ITEM_ITER_TX_TYPE(l_tx_item, TX_ITEM_TYPE_SIG, l_size, i, a_tx_in) {
+        l_pkey_sign = dap_chain_datum_tx_item_sig_get_sign((dap_chain_tx_sig_t *)l_tx_item);
+        l_sign_num++;
+    }
+    dap_sign_get_pkey_hash(l_pkey_sign, &l_pkey_hash);
+    if (--l_sign_num && dap_chain_datum_tx_verify_sign(a_tx_in, l_sign_num)) {
+        log_it(L_WARNING, "Last vote tx %s sign verification failed", dap_chain_hash_fast_to_str_static(a_tx_hash));
+        return -22;
     }
-    dap_chain_tx_sig_t *l_vote_sig = (dap_chain_tx_sig_t *)(dap_list_last(l_signs_list)->data);
-    dap_sign_get_pkey_hash((dap_sign_t*)l_vote_sig->sig, &pkey_hash);
-    dap_list_free(l_signs_list);
 
-    if (!a_apply) {
-        if (l_vote_tx_item->answer_idx > dap_list_length(l_voting->params->options)) {
-            log_it(L_WARNING, "Invalid vote option index %" DAP_UINT64_FORMAT_U " for vote tx %s",
-                                                    l_vote_tx_item->answer_idx, dap_chain_hash_fast_to_str_static(a_tx_hash));
-            return -6;
-        }
-        if (l_voting->params->votes_max_count && dap_list_length(l_voting->votes) >= l_voting->params->votes_max_count){
-            log_it(L_WARNING, "The required number of votes has been collected for voting %s", dap_chain_hash_fast_to_str_static(&l_voting->hash));
-            return -7;
-        }
-        if (l_voting->params->voting_expire && l_voting->params->voting_expire <= a_tx_in->header.ts_created) {
-            log_it(L_WARNING, "The voting %s has been expired", dap_chain_hash_fast_to_str_static(&l_voting->hash));
-            return -8;
-        }
+    if (l_vote_tx_item->answer_idx > dap_list_length(l_voting->params->options)) {
+        log_it(L_WARNING, "Invalid vote option index %" DAP_UINT64_FORMAT_U " for vote tx %s",
+                                                l_vote_tx_item->answer_idx, dap_chain_hash_fast_to_str_static(a_tx_hash));
+        return -6;
+    }
+    if (l_voting->params->votes_max_count && dap_list_length(l_voting->votes) >= l_voting->params->votes_max_count){
+        log_it(L_WARNING, "The required number of votes has been collected for voting %s", dap_chain_hash_fast_to_str_static(&l_voting->hash));
+        return -7;
+    }
+    if (l_voting->params->voting_expire && l_voting->params->voting_expire <= a_tx_in->header.ts_created) {
+        log_it(L_WARNING, "The voting %s has been expired", dap_chain_hash_fast_to_str_static(&l_voting->hash));
+        return -8;
+    }
 
-        if (l_voting->params->delegate_key_required &&
-                !dap_chain_net_srv_stake_check_pkey_hash(a_ledger->net->pub.id, &pkey_hash)){
-            log_it(L_WARNING, "Voting %s required a delegated key", dap_chain_hash_fast_to_str_static(&l_voting->hash));
-            return -10;
-        }
+    if (l_voting->params->delegate_key_required &&
+            !dap_chain_net_srv_stake_check_pkey_hash(a_ledger->net->pub.id, &l_pkey_hash)){
+        log_it(L_WARNING, "Voting %s required a delegated key", dap_chain_hash_fast_to_str_static(&l_voting->hash));
+        return -10;
     }
 
     dap_list_t *l_vote_overwrited = NULL;
     for (dap_list_t *it = l_voting->votes; it; it = it->next) {
-        if (dap_hash_fast_compare(&((struct vote *)it->data)->pkey_hash, &pkey_hash)) {
+        if (dap_hash_fast_compare(&((struct vote *)it->data)->pkey_hash, &l_pkey_hash)) {
             dap_hash_fast_t *l_vote_hash = &((struct vote *)it->data)->vote_hash;
             if (!l_voting->params->vote_changing_allowed) {
                 char l_vote_hash_str[DAP_HASH_FAST_STR_SIZE];
@@ -380,15 +388,23 @@ static int s_vote_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx
         case TX_ITEM_TYPE_TSD: {    // check out conds
             dap_tsd_t *l_tsd = (dap_tsd_t *)((dap_chain_tx_tsd_t *)l_item)->tsd;
             if (l_tsd->type != VOTING_TSD_TYPE_VOTE_TX_COND)
-                continue;
+                return -14;
             l_tx_hash = ((dap_chain_tx_voting_tx_cond_t *)l_tsd->data)->tx_hash;
             l_out_idx = ((dap_chain_tx_voting_tx_cond_t *)l_tsd->data)->out_idx;
+            dap_chain_datum_tx_t *l_tx_prev_temp = dap_ledger_tx_find_by_hash(a_ledger, &l_tx_hash);
+            dap_chain_tx_out_cond_t *l_prev_out = (dap_chain_tx_out_cond_t *)dap_chain_datum_tx_out_get_by_out_idx(l_tx_prev_temp, l_out_idx);
+            if (!l_prev_out || l_prev_out->header.item_type != TX_ITEM_TYPE_OUT_COND ||
+                    l_prev_out->header.subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE)
+                return -16;
+            if (!dap_ledger_check_condition_owner(a_ledger, &l_tx_hash, l_prev_out->header.subtype, l_out_idx, l_pkey_sign))
+                return -17;
             break;
         }
         default:
             continue;
         }
-        uint256_t l_uncoloured_value = dap_ledger_coin_get_uncoloured_value(a_ledger, &l_vote_tx_item->voting_hash, &l_tx_hash, l_out_idx);
+        uint256_t l_uncoloured_value = dap_ledger_coin_get_uncoloured_value(a_ledger, &l_vote_tx_item->voting_hash, &l_tx_hash, l_out_idx,
+                                                                            l_vote_overwrited ? &l_pkey_hash : NULL);
         if (IS_ZERO_256(l_uncoloured_value)) {
             log_it(L_ERROR, "Coin with OUT number %d of tx %s is voted before in voting %s", l_out_idx, dap_chain_hash_fast_to_str_static(&l_tx_hash),
                                                                             dap_chain_hash_fast_to_str_static(&l_vote_tx_item->voting_hash));
@@ -409,35 +425,29 @@ static int s_vote_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx
     if (a_apply) {
         struct vote *l_vote_item = DAP_NEW_Z_RET_VAL_IF_FAIL(struct vote, -DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY);
         l_vote_item->vote_hash = *a_tx_hash;
-        l_vote_item->pkey_hash = pkey_hash;
+        l_vote_item->pkey_hash = l_pkey_hash;
         l_vote_item->answer_idx = l_vote_tx_item->answer_idx;
         l_vote_item->weight = l_weight;
 
         if (l_vote_overwrited) {
-            SUM_256_256(l_vote_item->weight, ((struct vote *)l_vote_overwrited->data)->weight, &l_vote_item->weight);
             // change vote & move it to the end of list
-            l_voting->votes = dap_list_remove_link(l_voting->votes, l_vote_overwrited);
+            const char *l_vote_hash_str = dap_hash_fast_to_str_static(&((struct vote *)l_vote_overwrited->data)->vote_hash);
             DAP_DELETE(l_vote_overwrited->data);
+            l_voting->votes = dap_list_delete_link(l_voting->votes, l_vote_overwrited);
+            log_it(L_NOTICE, "Vote %s of voting %s has been changed", l_vote_hash_str, dap_hash_fast_to_str_static(&l_voting->hash));
+        } else {
+            const char *l_vote_hash_str = dap_hash_fast_to_str_static(a_tx_hash);
+            log_it(L_NOTICE, "Vote %s of voting %s has been accepted", l_vote_hash_str, dap_hash_fast_to_str_static(&l_voting->hash));
         }
+
         l_voting->votes = dap_list_append(l_voting->votes, l_vote_item);
-        char l_vote_hash_str[DAP_HASH_FAST_STR_SIZE];
-        dap_hash_fast_to_str(a_tx_hash, l_vote_hash_str, DAP_HASH_FAST_STR_SIZE);
-        log_it(L_INFO, "Vote %s of voting %s has been %s", l_vote_hash_str, dap_hash_fast_to_str_static(&l_voting->hash),
-                                     l_vote_overwrited ? "changed" : "accepted");
+
     }
+    if (a_pkey_hash)
+        *a_pkey_hash = l_voting->params->vote_changing_allowed ? l_pkey_hash : (dap_hash_fast_t) { };
     return DAP_LEDGER_CHECK_OK;
 }
 
-int s_voting_ledger_verificator_callback(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash, bool a_apply)
-{
-    if (a_type == TX_ITEM_TYPE_VOTING)
-        return s_voting_verificator(a_ledger, a_tx_in, a_tx_hash, a_apply);
-    if (a_type == TX_ITEM_TYPE_VOTE)
-        return s_vote_verificator(a_ledger, a_tx_in, a_tx_hash, a_apply);
-    log_it(L_ERROR, "Item %d is not supported in votings", a_type);
-    return -3;
-}
-
 static inline bool s_vote_delete(dap_chain_net_id_t a_net_id, dap_chain_datum_tx_t *a_vote_tx, dap_hash_fast_t *a_vote_tx_hash)
 {
     dap_chain_tx_vote_t *l_vote_tx_item = (dap_chain_tx_vote_t *)dap_chain_datum_tx_item_get(a_vote_tx, NULL, NULL, TX_ITEM_TYPE_VOTE, NULL);
@@ -559,6 +569,7 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply)
         const char* l_max_votes_count_str = NULL;
         const char* l_fee_str = NULL;
         const char* l_wallet_str = NULL;
+        const char *l_token_str = NULL;
 
         dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-question", &l_question_str);
         if (!l_question_str){
@@ -591,8 +602,6 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply)
             return -DAP_CHAIN_NET_VOTE_CREATE_CONTAIN_MAX_OPTIONS;
         }
 
-        dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-expire", &l_voting_expire_str);
-        dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-max_votes_count", &l_max_votes_count_str);
         dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-fee", &l_fee_str);
         if (!l_fee_str){
             dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_FEE_PARAM_NOT_VALID, "Voting requires paramete -fee to be valid.");
@@ -606,6 +615,7 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply)
             return -DAP_CHAIN_NET_VOTE_CREATE_WALLET_PARAM_NOT_VALID;
         }
 
+        dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-expire", &l_voting_expire_str);
         dap_time_t l_time_expire = 0;
         if (l_voting_expire_str)
             l_time_expire = dap_time_from_str_rfc822(l_voting_expire_str);
@@ -614,6 +624,8 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply)
                                     "Wrong time format. -expire parameter must be in format \"Day Month Year HH:MM:SS Timezone\" e.g. \"19 August 2024 22:00:00 +00\"");
             return -DAP_CHAIN_NET_VOTE_CREATE_WRONG_TIME_FORMAT;
         }
+
+        dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-max_votes_count", &l_max_votes_count_str);
         uint64_t l_max_count = 0;
         if (l_max_votes_count_str)
             l_max_count = strtoul(l_max_votes_count_str, NULL, 10);
@@ -621,14 +633,25 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply)
         bool l_is_delegated_key = dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-delegated_key_required", NULL) ? true : false;
         bool l_is_vote_changing_allowed = dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-vote_changing_allowed", NULL) ? true : false;
         const char *c_wallets_path = dap_chain_wallet_get_path(g_config);
-        dap_chain_wallet_t *l_wallet_fee = dap_chain_wallet_open(l_wallet_str, c_wallets_path,NULL);
+        dap_chain_wallet_t *l_wallet_fee = dap_chain_wallet_open(l_wallet_str, c_wallets_path, NULL);
         if (!l_wallet_fee) {
             dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_WALLET_DOES_NOT_EXIST, "Wallet %s does not exist", l_wallet_str);
             return -DAP_CHAIN_NET_VOTE_CREATE_WALLET_DOES_NOT_EXIST;
         }
 
         char *l_hash_ret = NULL;
-        int res = dap_chain_net_srv_voting_create(l_question_str, l_options_list, l_time_expire, l_max_count, l_value_fee, l_is_delegated_key, l_is_vote_changing_allowed, l_wallet_fee, l_net, l_hash_out_type, &l_hash_ret);
+        int res = dap_chain_net_srv_voting_create(l_question_str,
+                                                  l_options_list,
+                                                  l_time_expire,
+                                                  l_max_count,
+                                                  l_value_fee,
+                                                  l_is_delegated_key,
+                                                  l_is_vote_changing_allowed,
+                                                  l_wallet_fee,
+                                                  l_net,
+                                                  l_token_str,
+                                                  l_hash_out_type,
+                                                  &l_hash_ret);
         dap_list_free(l_options_list);
         dap_chain_wallet_close(l_wallet_fee);
 
@@ -719,8 +742,10 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply)
         }
 
         dap_hash_fast_t l_voting_hash = {};
-        dap_chain_hash_fast_from_str(l_hash_str, &l_voting_hash);
-
+        if (dap_chain_hash_fast_from_str(l_hash_str, &l_voting_hash)) {
+            dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_HASH_INVALID, "Hash string is not recognozed as hex of base58 hash");
+            return -DAP_CHAIN_NET_VOTE_VOTING_HASH_INVALID;
+        }
 
         dap_chain_hash_fast_t l_pkey_hash;
         dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-cert", &l_cert_name);
@@ -756,8 +781,8 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply)
         }
 
         const char *c_wallets_path = dap_chain_wallet_get_path(g_config);
-        dap_chain_wallet_t *l_wallet_fee = dap_chain_wallet_open(l_wallet_str, c_wallets_path,NULL);
-        if (!l_wallet_fee) {
+        dap_chain_wallet_t *l_wallet = dap_chain_wallet_open(l_wallet_str, c_wallets_path, NULL);
+        if (!l_wallet) {
             dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_WALLET_DOES_NOT_EXIST, "Wallet %s does not exist", l_wallet_str);
             return -DAP_CHAIN_NET_VOTE_VOTING_WALLET_DOES_NOT_EXIST;
         }
@@ -766,9 +791,9 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply)
 
         char *l_hash_tx;
 
-        int res = dap_chain_net_srv_vote_create(l_cert, l_value_fee, l_wallet_fee, &l_voting_hash, l_option_idx_count,
+        int res = dap_chain_net_srv_vote_create(l_cert, l_value_fee, l_wallet, &l_voting_hash, l_option_idx_count,
                                             l_net, l_hash_out_type, &l_hash_tx);
-        dap_chain_wallet_close(l_wallet_fee);
+        dap_chain_wallet_close(l_wallet);
 
         switch (res) {
             case DAP_CHAIN_NET_VOTE_VOTING_OK: {
@@ -788,16 +813,12 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply)
             case DAP_CHAIN_NET_VOTE_VOTING_ALREADY_EXPIRED: {
                 dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_ALREADY_EXPIRED, "This voting already expired.");
             } break;
-            case DAP_CHAIN_NET_VOTE_VOTING_NO_KEY_FOUND_IN_CERT: {
-                dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_NO_KEY_FOUND_IN_CERT, 
-                                                    "No key found in \"%s\" certificate", l_cert_name);                
-            } break;
-            case DAP_CHAIN_NET_VOTE_VOTING_CERT_REQUIRED: {
+             case DAP_CHAIN_NET_VOTE_VOTING_CERT_REQUIRED: {
                 dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_CERT_REQUIRED, 
                                                     "This voting required a delegated key. Parameter -cert must contain a valid certificate name");
             } break;
-            case DAP_CHAIN_NET_VOTE_VOTING_NO_PUBLIC_KEY_IN_CERT: {
-                dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_NO_PUBLIC_KEY_IN_CERT, 
+            case DAP_CHAIN_NET_VOTE_VOTING_NO_KEY_FOUND_IN_CERT: {
+                dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_NO_KEY_FOUND_IN_CERT,
                                                     "Can't serialize public key of certificate \"%s\"",
                                                     l_cert_name);
             } break;
@@ -859,6 +880,7 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply)
                                     json_object_new_string_len(dap_chain_hash_fast_to_str_static(&it->hash), sizeof(dap_hash_str_t)) );            
             json_object_object_add( json_obj_vote, "question", 
                                     json_object_new_string(it->params->question) );
+            json_object_object_add(json_obj_vote, "token", json_object_new_string(it->params->token_ticker));
             json_object_array_add(json_arr_voting_out, json_obj_vote);
         }
         json_object_array_add(*json_arr_reply, json_arr_voting_out);
@@ -908,6 +930,7 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply)
         json_object* json_vote_out = json_object_new_object();
         json_object_object_add(json_vote_out, "voting_tx", json_object_new_string_len(l_hash_str, sizeof(dap_hash_str_t)));
         json_object_object_add(json_vote_out, "question", json_object_new_string(l_voting->params->question));
+        json_object_object_add(json_vote_out, "token", json_object_new_string(l_voting->params->token_ticker));
         if (l_voting->params->voting_expire) {
             char l_tmp_buf[DAP_TIME_STR_SIZE];
             dap_time_to_str_rfc822(l_tmp_buf, DAP_TIME_STR_SIZE, l_voting->params->voting_expire);
@@ -952,7 +975,6 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply)
         const char *l_tw_coins, *l_tw_datoshi = dap_uint256_to_char(l_total_weight, &l_tw_coins);
         json_object_object_add(json_vote_out, "total_sum", json_object_new_string(l_tw_coins));
         json_object_object_add(json_vote_out, "total_sum_datoshi", json_object_new_string(l_tw_datoshi));
-        json_object_object_add(json_vote_out, "ticker", json_object_new_string(l_net->pub.native_ticker));
         json_object_array_add(*json_arr_reply, json_vote_out);
     } break;
     default:
@@ -962,33 +984,25 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply)
 }
 
 int dap_chain_net_srv_voting_create(const char *a_question, dap_list_t *a_options, dap_time_t a_expire_vote,
-                              uint64_t a_max_vote, uint256_t a_fee, bool a_delegated_key_required,
-                              bool a_vote_changing_allowed, dap_chain_wallet_t *a_wallet,
-                              dap_chain_net_t *a_net, const char *a_hash_out_type, char **a_hash_output)
+                                    uint64_t a_max_vote, uint256_t a_fee, bool a_delegated_key_required,
+                                    bool a_vote_changing_allowed, dap_chain_wallet_t *a_wallet,
+                                    dap_chain_net_t *a_net, const char *a_token_ticker,
+                                    const char *a_hash_out_type, char **a_hash_output)
 {
 
-    if (strlen(a_question) > DAP_CHAIN_DATUM_TX_VOTING_QUESTION_MAX_LENGTH){
+    if (strlen(a_question) > DAP_CHAIN_DATUM_TX_VOTING_QUESTION_MAX_LENGTH)
         return DAP_CHAIN_NET_VOTE_CREATE_LENGTH_QUESTION_OVERSIZE_MAX;
-    }
 
     // Parse options list
-
-    if(dap_list_length(a_options) > DAP_CHAIN_DATUM_TX_VOTING_OPTION_MAX_COUNT){
+    if (dap_list_length(a_options) > DAP_CHAIN_DATUM_TX_VOTING_OPTION_MAX_COUNT)
         return DAP_CHAIN_NET_VOTE_CREATE_COUNT_OPTION_OVERSIZE_MAX;
-    }
 
-    if (IS_ZERO_256(a_fee)) {
+    if (IS_ZERO_256(a_fee))
         return DAP_CHAIN_NET_VOTE_CREATE_FEE_IS_ZERO;
-    }
-
-    dap_enc_key_t *l_priv_key = NULL;
-    l_priv_key = dap_chain_wallet_get_key(a_wallet, 0);
 
     const dap_chain_addr_t *l_addr_from = (const dap_chain_addr_t *) dap_chain_wallet_get_addr(a_wallet, a_net->pub.id);
-
-    if(!l_addr_from) {
+    if(!l_addr_from)
         return DAP_CHAIN_NET_VOTE_CREATE_SOURCE_ADDRESS_IS_INVALID;
-    }
 
     const char *l_native_ticker = a_net->pub.native_ticker;
     uint256_t l_net_fee = {}, l_total_fee = {}, l_value_transfer;
@@ -1081,6 +1095,16 @@ int dap_chain_net_srv_voting_create(const char *a_question, dap_list_t *a_option
         DAP_DEL_Z(l_vote_changing_item);
     }
 
+    if (a_token_ticker) {
+        dap_chain_tx_tsd_t *l_voting_token_item = dap_chain_datum_voting_token_tsd_create(a_token_ticker);
+        if (!l_voting_token_item) {
+            dap_chain_datum_tx_delete(l_tx);
+            return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_CREATE_TSD_TOKEN;
+        }
+        dap_chain_datum_tx_add_item(&l_tx, l_voting_token_item);
+        DAP_DEL_Z(l_voting_token_item);
+    }
+
     // add 'in' items
     uint256_t l_value_to_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_used_out);
     assert(EQUAL_256(l_value_to_items, l_value_transfer));
@@ -1088,7 +1112,7 @@ int dap_chain_net_srv_voting_create(const char *a_question, dap_list_t *a_option
     uint256_t l_value_pack = {};
     // Network fee
     if (l_net_fee_used) {
-        if (dap_chain_datum_tx_add_out_item(&l_tx, &l_addr_fee, l_net_fee) == 1)
+        if (dap_chain_datum_tx_add_out_ext_item(&l_tx, &l_addr_fee, l_net_fee, l_native_ticker) == 1)
             SUM_256_256(l_value_pack, l_net_fee, &l_value_pack);
         else {
             dap_chain_datum_tx_delete(l_tx);
@@ -1108,17 +1132,20 @@ int dap_chain_net_srv_voting_create(const char *a_question, dap_list_t *a_option
     uint256_t l_value_back;
     SUBTRACT_256_256(l_value_transfer, l_value_pack, &l_value_back);
     if(!IS_ZERO_256(l_value_back)) {
-        if(dap_chain_datum_tx_add_out_item(&l_tx, l_addr_from, l_value_back) != 1) {
+        if(dap_chain_datum_tx_add_out_ext_item(&l_tx, l_addr_from, l_value_back, l_native_ticker) != 1) {
             dap_chain_datum_tx_delete(l_tx);
             return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_ADD_OUT_WITH_VALUE_BACK;
         }
     }
 
     // add 'sign' items
-    if(dap_chain_datum_tx_add_sign_item(&l_tx, l_priv_key) != 1) {
+    dap_enc_key_t *l_priv_key = dap_chain_wallet_get_key(a_wallet, 0);
+    if (dap_chain_datum_tx_add_sign_item(&l_tx, l_priv_key) != 1) {
         dap_chain_datum_tx_delete(l_tx);
+        dap_enc_key_delete(l_priv_key);
         return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_SIGNED_TX;
     }
+    dap_enc_key_delete(l_priv_key);
 
     size_t l_tx_size = dap_chain_datum_tx_get_size(l_tx);
     dap_hash_fast_t l_tx_hash;
@@ -1141,8 +1168,6 @@ int dap_chain_net_srv_vote_create(dap_cert_t *a_cert, uint256_t a_fee, dap_chain
                               uint64_t a_option_idx, dap_chain_net_t *a_net, const char *a_hash_out_type,
                               char **a_hash_tx_out)
 {
-
-
     struct voting *l_voting = s_voting_find(a_net->pub.id, a_voting_hash);
     if (!l_voting)
         return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_FIND_VOTE;
@@ -1153,47 +1178,44 @@ int dap_chain_net_srv_vote_create(dap_cert_t *a_cert, uint256_t a_fee, dap_chain
     if (l_voting->params->voting_expire && dap_time_now() > l_voting->params->voting_expire)
         return DAP_CHAIN_NET_VOTE_VOTING_ALREADY_EXPIRED;
 
+
+    dap_chain_addr_t *l_addr_from = dap_chain_wallet_get_addr(a_wallet, a_net->pub.id);
+    if (!l_addr_from)
+        return DAP_CHAIN_NET_VOTE_VOTING_SOURCE_ADDRESS_INVALID;
+
     dap_hash_fast_t l_pkey_hash = {0};
 
     if (l_voting->params->delegate_key_required) {
         if (!a_cert)
             return DAP_CHAIN_NET_VOTE_VOTING_CERT_REQUIRED;
-        if (!a_cert->enc_key)
+        if (dap_cert_get_pkey_hash(a_cert, &l_pkey_hash))
             return DAP_CHAIN_NET_VOTE_VOTING_NO_KEY_FOUND_IN_CERT;
-        // Get publivc key hash
-        size_t l_pub_key_size = 0;
-        uint8_t *l_pub_key = dap_enc_key_serialize_pub_key(a_cert->enc_key, &l_pub_key_size);;
-        if (l_pub_key == NULL)
-            return DAP_CHAIN_NET_VOTE_VOTING_NO_PUBLIC_KEY_IN_CERT;
-
-        dap_hash_fast(l_pub_key, l_pub_key_size, &l_pkey_hash);
-        DAP_DELETE(l_pub_key);
         if (!dap_chain_net_srv_stake_check_pkey_hash(a_net->pub.id, &l_pkey_hash))
             return DAP_CHAIN_NET_VOTE_VOTING_KEY_IS_NOT_DELEGATED;
-        for (dap_list_t *it = l_voting->votes; it; it = it->next)
-            if (dap_hash_fast_compare(&((struct vote *)it->data)->pkey_hash, &l_pkey_hash) &&
-                    !l_voting->params->vote_changing_allowed)
-                return DAP_CHAIN_NET_VOTE_VOTING_DOES_NOT_ALLOW_CHANGE_YOUR_VOTE;
-    }
-
-    dap_enc_key_t *l_priv_key = NULL;
-
-    l_priv_key = dap_chain_wallet_get_key(a_wallet, 0);
-
-    const dap_chain_addr_t *l_addr_from = (const dap_chain_addr_t *) dap_chain_wallet_get_addr(a_wallet, a_net->pub.id);
+    } else
+        l_pkey_hash = l_addr_from->data.hash_fast;
 
-    if (!l_addr_from)
-        return DAP_CHAIN_NET_VOTE_VOTING_SOURCE_ADDRESS_INVALID;
+    bool l_vote_changed = false;
+    for (dap_list_t *it = l_voting->votes; it; it = it->next)
+        if (dap_hash_fast_compare(&((struct vote *)it->data)->pkey_hash, &l_pkey_hash)) {
+            if (!l_voting->params->vote_changing_allowed)
+                return DAP_CHAIN_NET_VOTE_VOTING_DOES_NOT_ALLOW_CHANGE_YOUR_VOTE;
+            l_vote_changed = true;
+            break;
+        }
 
-    const char *l_native_ticker = a_net->pub.native_ticker;
-    uint256_t l_net_fee = {}, l_total_fee = {}, l_value_transfer;
+    const char *l_token_ticker = l_voting->params->token_ticker;
+    uint256_t l_net_fee = {}, l_total_fee = a_fee, l_value_transfer, l_fee_transfer;
     dap_chain_addr_t l_addr_fee = {};
     bool l_net_fee_used = dap_chain_net_tx_get_fee(a_net->pub.id, &l_net_fee, &l_addr_fee);
-    SUM_256_256(l_net_fee, a_fee, &l_total_fee);
-
-    dap_ledger_t* l_ledger = dap_ledger_by_net_name(a_net->pub.name);
-    dap_list_t *l_list_used_out = dap_ledger_get_list_tx_outs(l_ledger, l_native_ticker, l_addr_from, &l_value_transfer);
-    if (!l_list_used_out || compare256(l_value_transfer, l_total_fee) <= 0) {
+    if (l_net_fee_used)
+        SUM_256_256(l_net_fee, a_fee, &l_total_fee);
+
+    bool l_native_tx = !dap_strcmp(l_token_ticker, a_net->pub.native_ticker);
+    dap_ledger_t *l_ledger = a_net->pub.ledger;
+    dap_list_t *l_list_used_out = dap_ledger_get_list_tx_outs(l_ledger, l_token_ticker, l_addr_from, &l_value_transfer);
+    if (!l_list_used_out || (l_native_tx && compare256(l_value_transfer, l_total_fee) < 0)) {
+        dap_list_free_full(l_list_used_out, NULL);
         return DAP_CHAIN_NET_VOTE_VOTING_NOT_ENOUGH_FUNDS_TO_TRANSFER;
     }
 
@@ -1203,16 +1225,17 @@ int dap_chain_net_srv_vote_create(dap_cert_t *a_cert, uint256_t a_fee, dap_chain
     DL_FOREACH_SAFE(l_list_used_out, it, tmp) {
         dap_chain_tx_used_out_item_t *l_out = (dap_chain_tx_used_out_item_t *)it->data;
         uint256_t l_uncoloured_value = dap_ledger_coin_get_uncoloured_value(a_net->pub.ledger, a_voting_hash,
-                                                                            &l_out->tx_hash_fast, l_out->num_idx_out);
+                                                                            &l_out->tx_hash_fast, l_out->num_idx_out,
+                                                                            l_vote_changed ? &l_pkey_hash : NULL);
         if (IS_ZERO_256(l_uncoloured_value)) {
-            dap_list_delete_link(l_list_used_out, it);
+            l_list_used_out = dap_list_delete_link(l_list_used_out, it);
             continue;
         }
         if (SUM_256_256(l_value_transfer_new, l_out->value, &l_value_transfer_new))
             return DAP_CHAIN_NET_VOTE_VOTING_INTEGER_OVERFLOW;
     }
 
-    if ((IS_ZERO_256(l_value_transfer_new) || compare256(l_value_transfer_new, l_total_fee) <= 0))
+    if (IS_ZERO_256(l_value_transfer_new) || (l_native_tx && compare256(l_value_transfer_new, l_total_fee) <= 0))
         return DAP_CHAIN_NET_VOTE_VOTING_UNSPENT_UTX0_FOR_PARTICIPATION_THIS_VOTING;
 
     l_value_transfer = l_value_transfer_new;
@@ -1220,6 +1243,27 @@ int dap_chain_net_srv_vote_create(dap_cert_t *a_cert, uint256_t a_fee, dap_chain
     // create empty transaction
     dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create();
 
+    // add 'in' items for fee
+    uint256_t l_value_back = l_value_transfer, l_fee_back = {};
+    if (!l_native_tx) {
+        dap_list_t *l_list_fee_outs = dap_chain_wallet_get_list_tx_outs_with_val(l_ledger, a_net->pub.native_ticker,
+                                                                                 l_addr_from, l_total_fee, &l_fee_transfer);
+        if (!l_list_fee_outs) {
+            dap_chain_datum_tx_delete(l_tx);
+            return DAP_CHAIN_NET_VOTE_VOTING_NOT_ENOUGH_FUNDS_TO_TRANSFER;
+        }
+        uint256_t l_value_fee_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_fee_outs);
+        assert(EQUAL_256(l_value_fee_items, l_fee_transfer));
+        dap_list_free_full(l_list_fee_outs, NULL);
+        SUBTRACT_256_256(l_fee_transfer, l_total_fee, &l_fee_back);
+    } else
+        SUBTRACT_256_256(l_value_transfer, l_total_fee, &l_value_back);
+
+    // add 'in' items
+    uint256_t l_value_to_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_used_out);
+    assert(EQUAL_256(l_value_to_items, l_value_transfer));
+    dap_list_free_full(l_list_used_out, NULL);
+
     // Add vote item
     if (a_option_idx > dap_list_length(l_voting->params->options)){
         dap_chain_datum_tx_delete(l_tx);
@@ -1233,24 +1277,26 @@ int dap_chain_net_srv_vote_create(dap_cert_t *a_cert, uint256_t a_fee, dap_chain
     dap_chain_datum_tx_add_item(&l_tx, l_vote_item);
     DAP_DEL_Z(l_vote_item);
 
-    // add stake out conds items
+    // add out conds items
     int err = 0;
-    dap_list_t *l_outs = dap_ledger_get_list_tx_cond_outs(l_ledger, l_addr_from);
+    dap_list_t *l_outs = dap_ledger_get_list_tx_cond_outs(l_ledger, DAP_CHAIN_TX_OUT_COND_SUBTYPE_ALL, l_token_ticker, l_addr_from);
     for (dap_list_t *it = l_outs; it; it = it->next) {
         dap_chain_tx_used_out_item_t *l_out_item = (dap_chain_tx_used_out_item_t *)it->data;
-        uint256_t l_value = dap_ledger_coin_get_uncoloured_value(l_ledger, a_voting_hash, &l_out_item->tx_hash_fast, l_out_item->num_idx_out);
-        if (IS_ZERO_256(l_value))
+        uint256_t l_uncoloured_value = dap_ledger_coin_get_uncoloured_value(l_ledger, a_voting_hash,
+                                                                            &l_out_item->tx_hash_fast, l_out_item->num_idx_out,
+                                                                            l_vote_changed ? &l_pkey_hash : NULL);
+        if (IS_ZERO_256(l_uncoloured_value))
             continue;
         dap_chain_tx_tsd_t *l_item = dap_chain_datum_voting_vote_tx_cond_tsd_create(l_out_item->tx_hash_fast, l_out_item->num_idx_out);
         if (!l_item) {
             err = DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_CREATE_TSD_TX_COND_ITEM;
             break;
         }
-        if (dap_chain_datum_tx_add_item(&l_tx, l_item) != 1) {
+        if (dap_chain_datum_tx_add_item(&l_tx, l_item) != 1)
             err = DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_CREATE_TSD_TX_COND_ITEM;
-            break;
-        }
         DAP_DEL_Z(l_item);
+        if (err)
+            break;
     }
     dap_list_free_full(l_outs, NULL);
     if (err) {
@@ -1258,53 +1304,44 @@ int dap_chain_net_srv_vote_create(dap_cert_t *a_cert, uint256_t a_fee, dap_chain
         return err;
     }
 
-    // add 'in' items
-    uint256_t l_value_to_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_used_out);
-    assert(EQUAL_256(l_value_to_items, l_value_transfer));
-    dap_list_free_full(l_list_used_out, NULL);
-    uint256_t l_value_pack = {};
     // Network fee
-    if (l_net_fee_used) {
-        if (dap_chain_datum_tx_add_out_item(&l_tx, &l_addr_fee, l_net_fee) == 1)
-            SUM_256_256(l_value_pack, l_net_fee, &l_value_pack);
-        else {
-            dap_chain_datum_tx_delete(l_tx);
-            return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_NET_FEE_OUT;
-        }
+    if (l_net_fee_used && dap_chain_datum_tx_add_out_ext_item(&l_tx, &l_addr_fee, l_net_fee, a_net->pub.native_ticker) != 1) {
+        dap_chain_datum_tx_delete(l_tx);
+        return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_NET_FEE_OUT;
     }
+
     // Validator's fee
-    if (!IS_ZERO_256(a_fee)) {
-        if (dap_chain_datum_tx_add_fee_item(&l_tx, a_fee) == 1)
-            SUM_256_256(l_value_pack, a_fee, &l_value_pack);
-        else {
-            dap_chain_datum_tx_delete(l_tx);
-            return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_NET_FEE_OUT;
-        }
+    if (!IS_ZERO_256(a_fee) && dap_chain_datum_tx_add_fee_item(&l_tx, a_fee) != 1) {
+        dap_chain_datum_tx_delete(l_tx);
+        return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_NET_FEE_OUT;
     }
+
     // coin back
-    uint256_t l_value_back;
-    SUBTRACT_256_256(l_value_transfer, l_value_pack, &l_value_back);
-    if(!IS_ZERO_256(l_value_back)) {
-        if(dap_chain_datum_tx_add_out_item(&l_tx, l_addr_from, l_value_back) != 1) {
-            dap_chain_datum_tx_delete(l_tx);
-            return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_OUT_WITH_VALUE_BACK;
-        }
+    if (!IS_ZERO_256(l_value_back) && dap_chain_datum_tx_add_out_ext_item(&l_tx, l_addr_from, l_value_back, l_token_ticker) != 1) {
+        dap_chain_datum_tx_delete(l_tx);
+        return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_OUT_WITH_VALUE_BACK;
+    }
+    if (!IS_ZERO_256(l_fee_back) && dap_chain_datum_tx_add_out_ext_item(&l_tx, l_addr_from, l_fee_back, a_net->pub.native_ticker) != 1) {
+        dap_chain_datum_tx_delete(l_tx);
+        return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_OUT_WITH_VALUE_BACK;
     }
 
+    dap_enc_key_t *l_priv_key = dap_chain_wallet_get_key(a_wallet, 0);
     // add 'sign' items with wallet sign
-    if(dap_chain_datum_tx_add_sign_item(&l_tx, l_priv_key) != 1) {
+    if (dap_chain_datum_tx_add_sign_item(&l_tx, l_priv_key) != 1) {
         dap_chain_datum_tx_delete(l_tx);
+        dap_enc_key_delete(l_priv_key);
         return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_SIGN_TX;
     }
+    dap_enc_key_delete(l_priv_key);
 
     // add 'sign' items with delegated key if needed
-    if(a_cert){
-        if(dap_chain_datum_tx_add_sign_item(&l_tx, a_cert->enc_key) != 1) {
-            dap_chain_datum_tx_delete(l_tx);
-            return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_SIGN_TX;
-        }
+    if (a_cert && dap_chain_datum_tx_add_sign_item(&l_tx, a_cert->enc_key) != 1) {
+        dap_chain_datum_tx_delete(l_tx);
+        return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_SIGN_TX;
     }
 
+
     size_t l_tx_size = dap_chain_datum_tx_get_size(l_tx);
     dap_hash_fast_t l_tx_hash;
     dap_hash_fast(l_tx, l_tx_size, &l_tx_hash);
diff --git a/modules/service/voting/include/dap_chain_net_srv_voting.h b/modules/service/voting/include/dap_chain_net_srv_voting.h
index 89c80746ac..1fc6505066 100644
--- a/modules/service/voting/include/dap_chain_net_srv_voting.h
+++ b/modules/service/voting/include/dap_chain_net_srv_voting.h
@@ -83,13 +83,14 @@ enum DAP_CHAIN_NET_SRV_VOTING_CLI_ERRORS {
     DAP_CHAIN_NET_VOTE_CREATE_WALLET_PARAM_NOT_VALID,
     DAP_CHAIN_NET_VOTE_CREATE_WALLET_DOES_NOT_EXIST,
     DAP_CHAIN_NET_VOTE_CREATE_WRONG_TIME_FORMAT,
+    DAP_CHAIN_NET_VOTE_CREATE_WRONG_TOKEN,
+    DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_CREATE_TSD_TOKEN,
 
     DAP_CHAIN_NET_VOTE_VOTING_OK,
     DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_FIND_VOTE,
     DAP_CHAIN_NET_VOTE_VOTING_THIS_VOTING_HAVE_MAX_VALUE_VOTES,
     DAP_CHAIN_NET_VOTE_VOTING_ALREADY_EXPIRED,
     DAP_CHAIN_NET_VOTE_VOTING_NO_KEY_FOUND_IN_CERT,
-    DAP_CHAIN_NET_VOTE_VOTING_NO_PUBLIC_KEY_IN_CERT,
     DAP_CHAIN_NET_VOTE_VOTING_CERT_REQUIRED,
     DAP_CHAIN_NET_VOTE_VOTING_KEY_IS_NOT_DELEGATED,
     DAP_CHAIN_NET_VOTE_VOTING_DOES_NOT_ALLOW_CHANGE_YOUR_VOTE,
@@ -106,6 +107,7 @@ enum DAP_CHAIN_NET_SRV_VOTING_CLI_ERRORS {
     DAP_CHAIN_NET_VOTE_VOTING_NET_PARAM_MISSING,
     DAP_CHAIN_NET_VOTE_VOTING_NET_PARAM_NOT_VALID,
     DAP_CHAIN_NET_VOTE_VOTING_HASH_NOT_FOUND,
+    DAP_CHAIN_NET_VOTE_VOTING_HASH_INVALID,
     DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_FIND_CERT,
     DAP_CHAIN_NET_VOTE_VOTING_FEE_PARAM_NOT_VALID,
     DAP_CHAIN_NET_VOTE_VOTING_FEE_PARAM_BAD_TYPE,
@@ -130,9 +132,10 @@ uint64_t *dap_chain_net_srv_voting_get_result(dap_ledger_t* a_ledger, dap_chain_
 dap_time_t dap_chain_net_srv_voting_get_expiration_time(dap_ledger_t *a_ledger, dap_chain_hash_fast_t *a_voting_hash);
 
 int dap_chain_net_srv_voting_create(const char *a_question, dap_list_t *a_options, dap_time_t a_expire_vote,
-                             uint64_t a_max_vote, uint256_t a_fee, bool a_delegated_key_required,
-                             bool a_vote_changing_allowed, dap_chain_wallet_t *a_wallet,
-                             dap_chain_net_t *a_net, const char *a_hash_out_type, char **a_hash_output);
+                                    uint64_t a_max_vote, uint256_t a_fee, bool a_delegated_key_required,
+                                    bool a_vote_changing_allowed, dap_chain_wallet_t *a_wallet,
+                                    dap_chain_net_t *a_net, const char *a_token_ticker,
+                                    const char *a_hash_out_type, char **a_hash_output);
 int dap_chain_net_srv_vote_create(dap_cert_t *a_cert, uint256_t a_fee, dap_chain_wallet_t *a_wallet, dap_hash_fast_t *a_voting_hash,
                               uint64_t a_option_idx, dap_chain_net_t *a_net, const char *a_hash_out_type,
                               char **a_hash_tx_out);
-- 
GitLab