diff --git a/README.md b/README.md
index a109f1795e57a23dd4a9f0c25508f536e021e78c..ff660379e1a86da3495874667a48249b22efa4e1 100644
--- a/README.md
+++ b/README.md
@@ -1,92 +1,14 @@
 # pycfhelpers
 
+Python SDK wrapper module. Mandatory for each plugin.
 
+Consists of two parts:
 
-## Getting started
+### common 
+common helper functions.\
+In common part, you cannot use any imports from the node SDK.
 
-To make it easy for you to get started with GitLab, here's a list of recommended next steps.
+### node 
+tools for working with nodes
 
-Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
-
-## Add your files
-
-- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
-- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
-
-```
-cd existing_repo
-git remote add origin https://gitlab.demlabs.net/cellframe/pycfhelpers.git
-git branch -M main
-git push -uf origin main
-```
-
-## Integrate with your tools
-
-- [ ] [Set up project integrations](https://gitlab.demlabs.net/cellframe/pycfhelpers/-/settings/integrations)
-
-## Collaborate with your team
-
-- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
-- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
-- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
-- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
-- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
-
-## Test and Deploy
-
-Use the built-in continuous integration in GitLab.
-
-- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
-- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
-- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
-- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
-- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
-
-***
-
-# Editing this README
-
-When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
-
-## Suggestions for a good README
-Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
-
-## Name
-Choose a self-explaining name for your project.
-
-## Description
-Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
-
-## Badges
-On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
-
-## Visuals
-Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
-
-## Installation
-Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
-
-## Usage
-Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
-
-## Support
-Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
-
-## Roadmap
-If you have ideas for releases in the future, it is a good idea to list them in the README.
-
-## Contributing
-State if you are open to contributions and what your requirements are for accepting them.
-
-For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
-
-You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
-
-## Authors and acknowledgment
-Show your appreciation to those who have contributed to the project.
-
-## License
-For open source projects, say how it is licensed.
-
-## Project status
-If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
+NOTE: pychelpers root directory files will be removed.
\ No newline at end of file
diff --git a/cellframenet.py b/cellframenet.py
index 3d7f8777f6d629c7dfcbf5aa03c180aaaa111988..bf1a39900d12318cf319dd4d073b1faabff297e5 100644
--- a/cellframenet.py
+++ b/cellframenet.py
@@ -3,7 +3,7 @@ from typing import Iterator
 import CellFrame
 from CellFrame.Chain import ChainAtomPtr
 from CellFrame.Network import Net
-from CellFrame.Common import Datum, DatumIter, DatumTx, DatumToken, DatumEmission, DatumAnchor, DatumDecree
+from CellFrame.Common import Datum, DatumTx, DatumToken, DatumEmission, DatumAnchor, DatumDecree
 from DAP import Crypto
 from DAP.Crypto import Cert, Sign
 from CellFrame.Chain import Mempool, Wallet, Chain
diff --git a/cfnet_struct.py b/cfnet_struct.py
deleted file mode 100644
index 403d6717a6b4aed68e398dcc4590aa221eb00232..0000000000000000000000000000000000000000
--- a/cfnet_struct.py
+++ /dev/null
@@ -1,556 +0,0 @@
-from __future__ import annotations
-
-import binascii
-import json
-from typing import Iterator, NewType, Literal, Callable, Dict, TypeVar
-import traceback
-from functools import wraps
-from datetime import datetime
-from abc import ABC, abstractmethod
-
-from CellFrame.Chain import ChainAtomPtr, ChainAddr, Mempool, Wallet, Chain, Ledger
-from CellFrame.Common import Datum, DatumTx, DatumToken, DatumEmission, DatumAnchor, DatumDecree
-
-from CellFrame.Consensus import DAG, Block
-from CellFrame.Network import Net
-from CellFrame.Common import TxIn, TxInCond, TxOut, TxOutCond, TxPkey, TxSig, TxToken, TxReceipt, TxOutExt, TxTSD, \
-    TxOutCondSubtypeSrvStakeLock
-
-from DAP.Crypto import HashFast, Sign, Pkey
-from DAP.Core import logIt
-
-from shared.types import DatumTypes, ChainTypes, ItemTypes
-from .types import CFNetState, TSD
-
-# from .mappings import CFSubDatumBuilder, CFItemMapper
-
-OriginItemTypes = TxIn | TxInCond | TxOut | TxOutCond | TxPkey | TxSig | TxToken | TxReceipt | TxOutExt | TxTSD | TxOutCondSubtypeSrvStakeLock
-ticker = NewType("ticker", str)
-
-
-class CFNet:
-    # main: 'CFChain'
-    # zerochain: 'CFChain'
-
-    def __init__(self, name: str):
-        self.name = name
-        self._origin_net = Net.byName(name)
-        if not self._origin_net:
-            raise RuntimeError(f"No such net: {name}")
-        self.main = CFChain(self, 'main')
-        self.zerochain = CFChain(self, 'zerochain')
-
-    @property
-    def id(self) -> int:
-        return self._origin_net.id.long()
-
-    @property
-    def chains(self) -> list['CFChain']:
-        return [self.main, self.zerochain]
-
-    def get_ledger(self):
-        return CFLedger(self, self._origin_net.getLedger())
-
-    def register_gdbsync_notification_callback(self, callback, *args, **kwargs):
-        def callback_wraper(op_code, group, key, value, *other):
-            callback(self, op_code, group, key, value, *args, net=self, **kwargs)
-
-        self._origin_net.addNotify(callback_wraper, ())
-
-    def change_state(self, state: CFNetState):
-        if state == CFNetState.NET_STATE_OFFLINE:
-            self._origin_net.stop()
-        elif state == CFNetState.NET_STATE_ONLINE:
-            self._origin_net.start()
-        else:
-            raise NotImplemented("This state not implemented")
-
-
-class CFNets:
-    def __init__(self, nets: list[CFNet]):
-        self.nets = nets
-
-    def mempool_notificator(self, *args, chain_name: Literal["main", "zerochain", "all"] = "all", **kwargs):
-        """
-        Usage example:
-        @CFNets(NETS).mempool_notificator("to_args", chain_name="main", key="to_kwargs")
-        def on_mempool_change(op_code, group, key, value, *args, chain:CFChain, **kwargs):
-            pass
-        """
-
-        def wrapper(callback: Callable[[], None]):
-            logIt.message(f"inside {callback=}")
-            for net in self.nets:
-                if chain_name == "all":
-                    for chain in net.chains:
-                        chain.register_mempool_notification_callback(callback, *args, **kwargs)
-                else:
-                    getattr(net, chain_name).register_mempool_notification_callback(callback, *args, **kwargs)
-
-        return wrapper
-
-    def atom_notificator(self, *args, chain_name: Literal["main", "zerochain", "all"] = "all", **kwargs):
-        """
-        Usage example:
-        @CFNets(NETS).atom_notificator("to_args", chain_name="main", key="to_kwargs")
-        def on_new_atom(atom: CFBlock | CFEvent, size: int, *args, chain: CFChain, **kwargs):
-            pass
-        """
-
-        def wrapper(callback: Callable[[], None]):
-            for net in self.nets:
-                if chain_name == "all":
-                    for chain in net.chains:
-                        chain.register_atom_notification_callback(callback, *args, **kwargs)
-                else:
-                    getattr(net, chain_name).register_atom_notification_callback(callback, *args, **kwargs)
-
-        return wrapper
-
-    def gdbsync_notificator(self, *args, **kwargs):
-        """
-        Usage example:
-        @CFNets(NETS).gdbsync_notificator("to_args", key="to_kwargs")
-        def on_new_table_record(op_code, group, key, value, *args, net:CFNet, **kwargs):
-            pass
-        """
-
-        def wrapper(callback: Callable[[], None]):
-            for net in self.nets:
-                net.register_gdbsync_notification_callback(callback, *args, **kwargs)
-
-        return wrapper
-
-    def ledger_tx_notificator(self, *args, **kwargs):
-        """
-        Usage example:
-        @CFNets(NETS).ledger_tx_notificator("to_args", key="to_kwargs")
-        def on_new_ledger_transaction(ledger, tx, *args, net: CFNet, **kwargs):
-            pass
-        """
-
-        def wrapper(callback: Callable[[], None]):
-            for net in self.nets:
-                ledger = net.get_ledger()
-                ledger.register_ledger_tx_notification_callback(callback, *args, **kwargs)
-
-        return wrapper
-
-
-class CFChain:
-    def __init__(self, net: CFNet, chain_name: str):
-        self.net = net
-        self._origin_chain = net._origin_net.getChainByName(chain_name)
-        if not self._origin_chain:
-            raise RuntimeError(f"chain with name={chain_name} not found in net with name={net.name}")
-        self.type = self._origin_chain.getCSName()
-        self.name = chain_name
-
-    def get_atoms(self) -> Iterator[CFBlock | CFEvent]:
-        iterator = self._origin_chain.createAtomIter(False)
-        ptr = self._origin_chain.atomIterGetFirst(iterator)
-
-        if not ptr:
-            logIt.message("not ptr")
-            return []
-
-        atom, size = ptr
-        logIt.message("...")
-        while atom:
-            if size <= 0:
-                atom, size = self._origin_chain.atomIterGetNext(iterator)
-                continue
-            if self.type == ChainTypes.esbocs:
-                yield CFBlock(atom=atom, chain=self)
-            elif self.type == ChainTypes.dag_poa:
-                yield CFEvent(atom=atom, chain=self)
-            else:
-                raise TypeError(f"Invalid Chain type={self.type}")
-
-            atom, size = self._origin_chain.atomIterGetNext(iterator)
-
-    def get_datums(self) -> Iterator['CFDatum']:
-        for atom in self.get_atoms():
-            for datum in atom.get_datums():
-                yield datum
-
-    def get_transactions(self) -> Iterator[CFDatumTX]:
-        for datum in self.get_datums():
-            if datum._origin_datum.isDatumTX():
-                yield datum.get_sub_datum()
-
-    def register_mempool_notification_callback(self, callback, *args, **kwargs):
-        def callback_wraper(op_code, group, key, value, *other):
-            logIt.message(f"{other=}")
-            try:
-                callback(op_code, group, key, value, *args, chain=self, **kwargs)
-            except Exception:
-                logIt.error(f"Error = {traceback.format_exc()}")
-
-        try:
-            self._origin_chain.addMempoolNotify(callback_wraper, ())
-        except Exception:
-            logIt.error(f"Error = {traceback.format_exc()}")
-
-    def register_atom_notification_callback(self, callback, *args, **kwargs):
-        def callback_wraper(atom: ChainAtomPtr, size, *other):
-            try:
-                if self.type == ChainTypes.esbocs:
-                    cf_atom = CFBlock(atom, self)
-                elif self.type == ChainTypes.dag_poa:
-                    cf_atom = CFEvent(atom, self)
-                else:
-                    raise TypeError(f"Invalid Chain type={self.type}")
-                callback(cf_atom, size, *args, chain=self, **kwargs)
-            except Exception:
-                logIt.error(traceback.format_exc())
-
-        self._origin_chain.addAtomNotify(callback_wraper, ())
-
-
-class CFLedger:
-    def __init__(self, net: CFNet, ledger: Ledger = None):
-        self.net = net
-        self._origin_ledger = ledger or self.net.get_ledger()
-
-    def get_tx_ticker(self, datum: CFDatumTX) -> ticker | None:
-        return self._origin_ledger.txGetTokenTickerByHash(HashFast.fromString(str(datum.hash)))
-
-    def calc_address_balances(self, address: str) -> dict[ticker, str]:
-        res = {}
-        chain_addr = ChainAddr.fromStr(address)
-        tickers = self._origin_ledger.addrGetTokenTickerAll(chain_addr)
-        for ticker in tickers:
-            balance = str(self._origin_ledger.calcBalance(chain_addr, ticker))
-            res[ticker] = balance
-        return res
-
-    def register_ledger_tx_notification_callback(self, callback, *args, **kwargs):
-        def callback_wrapper(ledger, tx, *other):
-            callback(ledger, tx, *args, net=self.net, **kwargs)
-
-        self._origin_ledger.txAddNotify(callback_wrapper, (args, kwargs))
-
-
-class CFBlock:
-    def __init__(self, atom: ChainAtomPtr, chain: CFChain):
-        self._origin_atom = atom
-        if chain.type != ChainTypes.esbocs:
-            raise TypeError(f"Chain type={chain.type} does not contain blocks")
-        block = Block.fromAtom(self._origin_atom)
-        if not block:
-            raise RuntimeError(f"Not found block from atom")
-        self._origin_block = block
-
-        self.chain = chain
-        self.hash = str(block.hash)
-        self.created_at = block.created
-        self.version = block.version
-        self.cell_id = str(block.cellId)
-        # TODO: move chain_id CFBlock --> CFChain
-        self.chainid = str(block.chainId)
-        self.signs = [CFSign(sign, self.chain.net) for sign in block.signs]
-        self.meta = block.metaData
-
-    def get_datums(self) -> list['CFDatum']:
-        if self._origin_block.datums is None:
-            return []
-        return [CFDatum(self, datum) for datum in self._origin_block.datums]
-
-
-class CFEvent:
-    def __init__(self, atom: ChainAtomPtr, chain: CFChain):
-        self._origin_atom = atom
-        if chain.type != ChainTypes.dag_poa:
-            raise TypeError(f"Chain type={chain.type} does not contain events")
-        event = DAG.fromAtom(self._origin_atom)
-        if not event:
-            raise RuntimeError(f"Not found event from atom")
-        self._origin_event = event
-
-        self.chain = chain
-        self.hash = str(event.hash)
-        self.created_at = event.created
-        self.version = event.version
-        self.cell_id = str(event.cellId)
-        self.signs = [CFSign(sign, self.chain.net) for sign in event.signs]
-        self.round_id = event.roundId
-        self.hashes = list(map(str, event.links))
-
-    def get_datums(self) -> list['CFDatum']:
-        # Event has only one Datum
-        return [CFDatum(self, self._origin_event.datum)]
-
-
-class CFDatum:
-    __slots__ = ["_origin_datum", "hash", "type", "created_at", "atom", "size", "version"]
-
-    def __init__(self, atom: CFBlock | CFEvent, datum: Datum):
-        self._origin_datum = datum
-        self.size = datum.getSize()
-        self.version = datum.versionStr
-        try:
-            self.created_at = datum.tsCreated
-        except OSError:
-            self.created_at = datetime.fromtimestamp(0)
-            logIt.error(f"Datum [{datum.hash}] has invalid timestamp!")
-        self.type = datum.getTypeStr()
-        self.hash = str(datum.hash)
-        self.atom = atom
-
-    def get_sub_datum(
-            self) -> CFDatumTX | CFDatumToken | CFDatumEmission | CFDatumAnchor | CFDatumDecree | CFDatumCustom:
-        # FIXME: change struct
-        from .mappings import CFSubDatumBuilder
-        sub_datum = CFSubDatumBuilder(self.type).build(self)
-        # if isinstance(sub_datum, CFDatumEmission):
-        #     sub_datum
-        return sub_datum
-
-
-class SubDatum:
-    def __init__(self, parent_datum: CFDatum, sub_datum):
-        self._parent_datum = parent_datum
-        self._origin_sub_datum = sub_datum
-        self.type = "DEFAULT"
-        if sub_datum is None:
-            self.hash = parent_datum.hash
-            self.type = "CORRUPTED"
-            logIt.error(f"Datum type:{parent_datum.type} hash:{parent_datum.hash} is CORRUPTED")
-
-    @property
-    def net(self) -> CFNet:
-        return self._parent_datum.atom.chain.net
-
-
-T = TypeVar('T', bound='CFItem')
-
-
-class CFDatumTX(SubDatum):
-    """Sub datum"""
-
-    def __init__(self, parent_datum: CFDatum, sub_datum: DatumTx):
-        super().__init__(parent_datum, sub_datum)
-        if self.type == "CORRUPTED":
-            return
-        self.hash = str(sub_datum.hash)
-        self.created_at = sub_datum.dateCreated
-        ledger = self.net.get_ledger()
-        self.ticker = ledger.get_tx_ticker(self)
-        self.accepted = bool(ticker)
-
-    def get_items(self, filter_type: type[T] | None = None) -> list[T]:
-        from .mappings import CFItemMapper
-        all_items = [CFItemMapper.build(item, self.net) for item in self._origin_sub_datum.getItems()
-                     if item is not None]
-        if filter_type:
-            return [item for item in all_items if isinstance(item, filter_type)]
-        else:
-            return all_items
-
-
-class CFItem:
-    def __init__(self, origin_item: OriginItemTypes, type: ItemTypes, net: CFNet):
-        self._origin_item = origin_item
-        self.type = type.value
-        self.net = net
-
-
-class CFTxIn(CFItem):
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self.prev_hash = str(self._origin_item.prevHash)
-        self.prev_idx = self._origin_item.prevIdx
-
-
-class CFTxInCond(CFItem):
-    pass
-
-
-class CFTxOut(CFItem):
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self.address = str(self._origin_item.addr)
-        self.value = self._origin_item.value.coins
-
-
-class CFTxOutCond(CFItem):
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self.expires = self._origin_item.tsExpires
-        self.value = self._origin_item.value.coins
-        self.subtype = str(self._origin_item.typeSubtype)
-
-
-class CFTxPkey(CFItem):
-    pass
-
-
-class CFTxSig(CFItem):
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self.size = self._origin_item.sigSize
-        self.sign = CFSign(self._origin_item.sign, self.net)
-
-
-class CFTxToken(CFItem):
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self.ticker = self._origin_item.ticker
-        self.emission_hash = str(self._origin_item.tokenEmissionHash)
-        self.emission_chain_id = str(self._origin_item.tokenEmissionChainId)
-
-
-class CFTxReceipt(CFItem):
-    pass
-
-
-class CFTxOutExt(CFItem):
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self.address = str(self._origin_item.addr)
-        self.ticker = self._origin_item.token
-        self.value = self._origin_item.value.coins
-
-
-class CFTxTSD(CFItem):
-    pass
-
-
-class CFTxOutCondSubtypeSrvPay(CFItem):
-    pass
-
-
-class CFTxOutCondSubtypeSrvXchange(CFItem):
-    pass
-
-
-class CFTxOutCondSubtypeSrvStakeLock(CFItem):
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self.unlock = self._origin_item.timeUnlock
-        self.value = self._origin_item.value.coins
-        self.reinvest_percent = self._origin_item.reinvestPercent
-
-
-class CFTxOutCondSubtypeSrvStakePosDelegate(CFItem):
-    pass
-
-
-class CFDatumToken(SubDatum):
-    """Sub datum"""
-
-    def __init__(self, parent_datum: CFDatum, sub_datum: DatumToken):
-        super().__init__(parent_datum, sub_datum)
-        if self.type == "CORRUPTED":
-            return
-        self.ticker = sub_datum.ticker
-        self.type = sub_datum.typeStr
-        self.data = sub_datum.data
-        try:
-            self.signs = [CFSign(sign, self.net) for sign in sub_datum.signs]
-        except AttributeError as e:
-            self.signs = []
-
-
-class CFDatumEmission(SubDatum):
-    """Sub datum"""
-
-    def __init__(self, parent_datum: CFDatum, sub_datum: DatumEmission):
-        super().__init__(parent_datum, sub_datum)
-        if self.type == "CORRUPTED":
-            return
-        self.hash = str(sub_datum.hash)
-        self.version = sub_datum.version
-        self.type = sub_datum.typeStr
-        self.ticker = sub_datum.ticker
-        self.address = str(sub_datum.addr)
-        # TODO: Math --> CFMath
-        self.value = sub_datum.value.coins
-        if self.type == "TOKEN_EMISSION_TYPE_AUTH":
-            self.data = [CFSign(sign, self.net) for sign in sub_datum.signs]
-        else:
-            self.data = sub_datum.data
-        self.signs = [CFSign(sign, self.net) for sign in sub_datum.signs]
-        self.tsd = {}
-        for tsd_type in TSD:
-            tsd = self._origin_sub_datum.getTSD(tsd_type.value)
-            if tsd is None:
-                continue
-            try:
-                tsd_data = tsd.decode('utf-8')
-            except UnicodeDecodeError:
-                tsd_data = binascii.hexlify(tsd).decode('utf-8')
-            except:
-                logIt.error(f"Incorrect TSD data. Skip TSD with type={tsd_type}")
-                continue
-
-            if tsd_type == TSD.TYPE_DATA:
-                try:
-                    tsd_data = json.loads(tsd_data)
-                except:
-                    pass
-
-            self.tsd[tsd_type.name] = tsd_data
-
-
-class CFDatumDecree(SubDatum):
-    """Sub datum"""
-
-    def __init__(self, parent_datum: CFDatum, sub_datum: DatumDecree):
-        super().__init__(parent_datum, sub_datum)
-        if self.type == "CORRUPTED":
-            return
-        self.hash = str(sub_datum.hash)
-        self.created_at = sub_datum.tsCreated
-        self.type = sub_datum.typeStr
-        self.subtype = sub_datum.subtypeStr
-        self.signs = [CFSign(sign, self.net) for sign in sub_datum.signs]
-        # self.TSD = ...
-
-
-class CFDatumAnchor(SubDatum):
-    """Sub datum"""
-
-    def __init__(self, parent_datum: CFDatum, sub_datum: DatumAnchor):
-        super().__init__(parent_datum, sub_datum)
-        if self.type == "CORRUPTED":
-            return
-        self.hash = str(sub_datum.hash)
-        self.created_at = sub_datum.created
-        self.signs = [CFSign(sign, self.net) for sign in sub_datum.signs]
-
-
-class CFDatumCustom(SubDatum):
-    """
-    Sub datum
-    get data from dataRaw()
-    """
-
-    def __init__(self, parent_datum: CFDatum, sub_datum: bytes):
-        super().__init__(parent_datum, sub_datum)
-        if self.type == "CORRUPTED":
-            return
-        self._origin_sub_datum = None
-        self.data = sub_datum.hex()
-
-
-class CFSign:
-    __slots__ = ["_origin_sign", "net", "type", "pkey", "size", "pkey_hash", "address"]
-
-    def __init__(self, sign: Sign, net: CFNet):
-        self._origin_sign = sign
-        self.net = net
-        self.type = str(sign.type)
-        self.pkey = CFPkey(sign.pkey)
-        self.size = sign.size
-        self.pkey_hash = str(self._origin_sign.pkeyHash)
-        self.address = str(ChainAddr.fill(self._origin_sign.type, self._origin_sign.pkeyHash, self.net._origin_net.id))
-        # self.TSD = ...
-
-
-class CFPkey:
-    def __init__(self, pkey: Pkey):
-        self._origin_pkey = pkey
-        self.type = pkey.type
-        self.size = pkey.size
diff --git a/common/__init__.py b/common/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/clean_helpers.py b/common/parsers.py
similarity index 97%
rename from clean_helpers.py
rename to common/parsers.py
index 8ba757b337f419bbb34c29548ad67b586a6c8025..d4276010323f9d6cb83596bcd2fe3f9ef37e7f16 100644
--- a/clean_helpers.py
+++ b/common/parsers.py
@@ -14,4 +14,4 @@ def parse_cf_v1_address(address: str) -> tuple[int, int, int, bytes, bytes, byte
     summary_hash = hash.digest()
     if summary_hash != control_hash:
         raise ValueError(f"Address={address} not valid")
-    return version, net_id, sign_id, public_hash, summary_hash, control_hash
+    return version, net_id, sign_id, public_hash, summary_hash, control_hash
\ No newline at end of file
diff --git a/common/types.py b/common/types.py
new file mode 100644
index 0000000000000000000000000000000000000000..53fe12eed2f6abfc690688ac55419ad2d8b82a89
--- /dev/null
+++ b/common/types.py
@@ -0,0 +1,47 @@
+from enum import Enum
+
+
+# class MixinHasValue:
+#     _value2member_map_ = None
+#
+#     @classmethod
+#     def has_value(cls, value) -> bool:
+#         """
+#         check value in Enum
+#         example: ExampleEnum.has_value("test-string")
+#         """
+#         return value in cls._value2member_map_
+
+
+class ChainTypes(str, Enum):
+    esbocs = "esbocs"
+    dag_poa = "dag_poa"
+
+
+class DatumTypes(str, Enum):
+    DATUM_TX = "DATUM_TX"
+    DATUM_TOKEN_DECL = "DATUM_TOKEN_DECL"
+    DATUM_TOKEN_EMISSION = "DATUM_TOKEN_EMISSION"
+    DATUM_CUSTOM = "DATUM_CUSTOM"
+    DATUM_DECREE = "DATUM_DECREE"
+    DATUM_ANCHOR = "DATUM_ANCHOR"
+
+
+class ItemTypes(str, Enum):
+    TX_ITEM_TYPE_IN = "TX_ITEM_TYPE_IN"
+    TX_ITEM_TYPE_IN_COND = "TX_ITEM_TYPE_IN_COND"
+    TX_ITEM_TYPE_OUT = "TX_ITEM_TYPE_OUT"
+    TX_ITEM_TYPE_OUT_COND = "TX_ITEM_TYPE_OUT_COND"
+    TX_ITEM_TYPE_PKEY = "TX_ITEM_TYPE_PKEY"
+    TX_ITEM_TYPE_SIG = "TX_ITEM_TYPE_SIG"
+    TX_ITEM_TYPE_IN_EMS = "TX_ITEM_TYPE_IN_EMS"  # TxToken
+    TX_ITEM_TYPE_RECEIPT = "TX_ITEM_TYPE_RECEIPT"
+    TX_ITEM_TYPE_OUT_EXT = "TX_ITEM_TYPE_OUT_EXT"
+    TX_ITEM_TYPE_TSD = "TX_ITEM_TYPE_TSD"
+    DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY = "DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY"
+    DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK = "DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK"
+    DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_XCHANGE = "DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_XCHANGE"
+    DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE = "DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE"
+
+
+
diff --git a/node/__init__.py b/node/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..269b96381f3ee2aebcc09abdf0f4566771ff4210
--- /dev/null
+++ b/node/__init__.py
@@ -0,0 +1,10 @@
+from .net import CFNet, CFChain, CFLedger
+from .consensus import CFBlock, CFEvent
+from .crypto import CFSign, CFPkey
+from .datums import CFDatum, CFDatumTX, CFDatumToken, CFDatumEmission, CFDatumCustom, CFDatumDecree, CFDatumAnchor
+
+from .items import (
+    CFItem, CFTxIn, CFTxInCond, CFTxOut, CFTxOutCond, CFTxPkey, CFTxSig, CFTxToken, CFTxReceipt, CFTxOutExt, CFTxTSD,
+    CFTxOutCondSubtypeSrvPay, CFTxOutCondSubtypeSrvXchange, CFTxOutCondSubtypeSrvStakeLock,
+    CFTxOutCondSubtypeSrvStakePosDelegate
+)
diff --git a/node/consensus.py b/node/consensus.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c376c3631a197219ba7aa1d08fb96dc48d89b20
--- /dev/null
+++ b/node/consensus.py
@@ -0,0 +1,61 @@
+from __future__ import annotations
+from typing import TYPE_CHECKING
+
+from CellFrame.Chain import ChainAtomPtr, ChainAddr
+from CellFrame.Consensus import DAG, Block
+
+if TYPE_CHECKING:
+    from .net import CFChain
+from .datums import CFDatum
+from .crypto import CFSign
+from ..common.types import ChainTypes
+
+
+class CFBlock:
+    def __init__(self, atom: ChainAtomPtr, chain: CFChain):
+        self._origin_atom = atom
+        if chain.type != ChainTypes.esbocs:
+            raise TypeError(f"Chain type={chain.type} does not contain blocks")
+        block = Block.fromAtom(self._origin_atom)
+        if not block:
+            raise RuntimeError(f"Not found block from atom")
+        self._origin_block = block
+
+        self.chain = chain
+        self.hash = str(block.hash)
+        self.created_at = block.created
+        self.version = block.version
+        self.cell_id = str(block.cellId)
+        # TODO: move chain_id CFBlock --> CFChain
+        self.chainid = str(block.chainId)
+        self.signs = [CFSign(sign, self.chain.net) for sign in block.signs]
+        self.meta = block.metaData
+
+    def get_datums(self) -> list[CFDatum]:
+        if self._origin_block.datums is None:
+            return []
+        return [CFDatum(self, datum) for datum in self._origin_block.datums]
+
+
+class CFEvent:
+    def __init__(self, atom: ChainAtomPtr, chain: CFChain):
+        self._origin_atom = atom
+        if chain.type != ChainTypes.dag_poa:
+            raise TypeError(f"Chain type={chain.type} does not contain events")
+        event = DAG.fromAtom(self._origin_atom)
+        if not event:
+            raise RuntimeError(f"Not found event from atom")
+        self._origin_event = event
+
+        self.chain = chain
+        self.hash = str(event.hash)
+        self.created_at = event.created
+        self.version = event.version
+        self.cell_id = str(event.cellId)
+        self.signs = [CFSign(sign, self.chain.net) for sign in event.signs]
+        self.round_id = event.roundId
+        self.hashes = list(map(str, event.links))
+
+    def get_datums(self) -> list[CFDatum]:
+        # Event has only one Datum
+        return [CFDatum(self, self._origin_event.datum)]
diff --git a/node/crypto.py b/node/crypto.py
new file mode 100644
index 0000000000000000000000000000000000000000..fea15fb14dc5df76080bb49c3e31ae89833c996a
--- /dev/null
+++ b/node/crypto.py
@@ -0,0 +1,27 @@
+from __future__ import annotations
+from typing import TypeVar, TYPE_CHECKING
+from DAP.Crypto import Sign, Pkey
+from CellFrame.Chain import ChainAddr
+
+if TYPE_CHECKING:
+    from .net import CFNet
+
+
+class CFSign:
+    __slots__ = ["_origin_sign", "net", "type", "pkey", "size", "pkey_hash", "address"]
+
+    def __init__(self, sign: Sign, net: CFNet):
+        self._origin_sign = sign
+        self.net = net
+        self.type = str(sign.type)
+        self.pkey = CFPkey(sign.pkey)
+        self.size = sign.size
+        self.pkey_hash = str(self._origin_sign.pkeyHash)
+        self.address = str(ChainAddr.fill(self._origin_sign.type, self._origin_sign.pkeyHash, self.net._origin_net.id))
+
+
+class CFPkey:
+    def __init__(self, pkey: Pkey):
+        self._origin_pkey = pkey
+        self.type = pkey.type
+        self.size = pkey.size
diff --git a/node/datums.py b/node/datums.py
new file mode 100644
index 0000000000000000000000000000000000000000..a372f3e935dbe8ea69b28af77768c134a577a21d
--- /dev/null
+++ b/node/datums.py
@@ -0,0 +1,168 @@
+from __future__ import annotations
+
+from typing import TypeVar, TYPE_CHECKING
+import json
+import binascii
+from datetime import datetime
+
+from DAP.Core import logIt
+from CellFrame.Common import Datum, DatumTx, DatumToken, DatumDecree, DatumAnchor, DatumEmission
+
+if TYPE_CHECKING:
+    from .net import CFNet
+    from .consensus import CFBlock, CFEvent
+
+from .items import CFItem
+from .crypto import CFSign
+
+from .types import ticker, TSD
+
+
+class CFDatum:
+    __slots__ = ["_origin_datum", "hash", "type", "created_at", "atom", "size", "version"]
+
+    def __init__(self, atom: CFBlock | CFEvent, datum: Datum):
+        self._origin_datum = datum
+        self.size = datum.getSize()
+        self.version = datum.versionStr
+        try:
+            self.created_at = datum.tsCreated
+        except OSError:
+            self.created_at = datetime.fromtimestamp(0)
+            logIt.error(f"Datum [{datum.hash}] has invalid timestamp!")
+        self.type = datum.getTypeStr()
+        self.hash = str(datum.hash)
+        self.atom = atom
+
+    def get_sub_datum(
+            self) -> CFDatumTX | CFDatumToken | CFDatumEmission | CFDatumAnchor | CFDatumDecree | CFDatumCustom:
+        from .mappings import CFSubDatumBuilder
+        sub_datum = CFSubDatumBuilder(self.type).build(self)
+        return sub_datum
+
+
+class SubDatum:
+    def __init__(self, parent_datum: CFDatum, sub_datum):
+        self._parent_datum = parent_datum
+        self._origin_sub_datum = sub_datum
+        self.type = "DEFAULT"
+        if sub_datum is None:
+            self.hash = parent_datum.hash
+            self.type = "CORRUPTED"
+            logIt.error(f"Datum type:{parent_datum.type} hash:{parent_datum.hash} is CORRUPTED")
+
+    @property
+    def net(self) -> 'CFNet':
+        return self._parent_datum.atom.chain.net
+
+
+T = TypeVar('T', bound=CFItem)
+
+
+class CFDatumTX(SubDatum):
+    def __init__(self, parent_datum: CFDatum, sub_datum: DatumTx):
+        super().__init__(parent_datum, sub_datum)
+        if self.type == "CORRUPTED":
+            return
+        self.hash = str(sub_datum.hash)
+        self.created_at = sub_datum.dateCreated
+        ledger = self.net.get_ledger()
+        self.ticker = ledger.get_tx_ticker(self)
+        self.accepted = bool(ticker)
+
+    def get_items(self, filter_type: type[T] | None = None) -> list[T]:
+        from .mappings import CFItemMapper
+        all_items = [CFItemMapper.build(item, self.net) for item in self._origin_sub_datum.getItems()
+                     if item is not None]
+        if filter_type:
+            return [item for item in all_items if isinstance(item, filter_type)]
+        else:
+            return all_items
+
+
+class CFDatumToken(SubDatum):
+    def __init__(self, parent_datum: CFDatum, sub_datum: DatumToken):
+        super().__init__(parent_datum, sub_datum)
+        if self.type == "CORRUPTED":
+            return
+        self.ticker = sub_datum.ticker
+        self.type = sub_datum.typeStr
+        self.data = sub_datum.data
+        try:
+            self.signs = [CFSign(sign, self.net) for sign in sub_datum.signs]
+        except AttributeError as e:
+            self.signs = []
+
+
+class CFDatumEmission(SubDatum):
+    def __init__(self, parent_datum: CFDatum, sub_datum: DatumEmission):
+        super().__init__(parent_datum, sub_datum)
+        if self.type == "CORRUPTED":
+            return
+        self.hash = str(sub_datum.hash)
+        self.version = sub_datum.version
+        self.type = sub_datum.typeStr
+        self.ticker = sub_datum.ticker
+        self.address = str(sub_datum.addr)
+        # TODO: Math --> CFMath
+        self.value = sub_datum.value.coins
+        if self.type == "TOKEN_EMISSION_TYPE_AUTH":
+            self.data = [CFSign(sign, self.net) for sign in sub_datum.signs]
+        else:
+            self.data = sub_datum.data
+        self.signs = [CFSign(sign, self.net) for sign in sub_datum.signs]
+        self.tsd = {}
+        for tsd_type in TSD:
+            tsd = self._origin_sub_datum.getTSD(tsd_type.value)
+            if tsd is None:
+                continue
+            try:
+                tsd_data = tsd.decode('utf-8')
+            except UnicodeDecodeError:
+                tsd_data = binascii.hexlify(tsd).decode('utf-8')
+            except:
+                logIt.error(f"Incorrect TSD data. Skip TSD with type={tsd_type}")
+                continue
+
+            if tsd_type == TSD.TYPE_DATA:
+                try:
+                    tsd_data = json.loads(tsd_data)
+                except:
+                    pass
+
+            self.tsd[tsd_type.name] = tsd_data
+
+
+class CFDatumDecree(SubDatum):
+    def __init__(self, parent_datum: CFDatum, sub_datum: DatumDecree):
+        super().__init__(parent_datum, sub_datum)
+        if self.type == "CORRUPTED":
+            return
+        self.hash = str(sub_datum.hash)
+        self.created_at = sub_datum.tsCreated
+        self.type = sub_datum.typeStr
+        self.subtype = sub_datum.subtypeStr
+        self.signs = [CFSign(sign, self.net) for sign in sub_datum.signs]
+
+
+class CFDatumAnchor(SubDatum):
+    def __init__(self, parent_datum: CFDatum, sub_datum: DatumAnchor):
+        super().__init__(parent_datum, sub_datum)
+        if self.type == "CORRUPTED":
+            return
+        self.hash = str(sub_datum.hash)
+        self.created_at = sub_datum.created
+        self.signs = [CFSign(sign, self.net) for sign in sub_datum.signs]
+
+
+class CFDatumCustom(SubDatum):
+    """
+    get data from dataRaw()
+    """
+
+    def __init__(self, parent_datum: CFDatum, sub_datum: bytes):
+        super().__init__(parent_datum, sub_datum)
+        if self.type == "CORRUPTED":
+            return
+        self._origin_sub_datum = None
+        self.data = sub_datum.hex()
diff --git a/node/items.py b/node/items.py
new file mode 100644
index 0000000000000000000000000000000000000000..0e1dfe8349916c66aef448b6a36efa00830a7bbf
--- /dev/null
+++ b/node/items.py
@@ -0,0 +1,97 @@
+from __future__ import annotations
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from .net import CFNet
+    from .types import OriginItemTypes
+    from ..common.types import ItemTypes
+
+from .crypto import CFSign
+
+
+class CFItem:
+    def __init__(self, origin_item: OriginItemTypes, type: ItemTypes, net: CFNet):
+        self._origin_item = origin_item
+        self.type = type.value
+        self.net = net
+
+
+class CFTxIn(CFItem):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.prev_hash = str(self._origin_item.prevHash)
+        self.prev_idx = self._origin_item.prevIdx
+
+
+class CFTxInCond(CFItem):
+    pass
+
+
+class CFTxOut(CFItem):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.address = str(self._origin_item.addr)
+        self.value = self._origin_item.value.coins
+
+
+class CFTxOutCond(CFItem):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.expires = self._origin_item.tsExpires
+        self.value = self._origin_item.value.coins
+        self.subtype = str(self._origin_item.typeSubtype)
+
+
+class CFTxPkey(CFItem):
+    pass
+
+
+class CFTxSig(CFItem):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.size = self._origin_item.sigSize
+        self.sign = CFSign(self._origin_item.sign, self.net)
+
+
+class CFTxToken(CFItem):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.ticker = self._origin_item.ticker
+        self.emission_hash = str(self._origin_item.tokenEmissionHash)
+        self.emission_chain_id = str(self._origin_item.tokenEmissionChainId)
+
+
+class CFTxReceipt(CFItem):
+    pass
+
+
+class CFTxOutExt(CFItem):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.address = str(self._origin_item.addr)
+        self.ticker = self._origin_item.token
+        self.value = self._origin_item.value.coins
+
+
+class CFTxTSD(CFItem):
+    pass
+
+
+class CFTxOutCondSubtypeSrvPay(CFItem):
+    pass
+
+
+class CFTxOutCondSubtypeSrvXchange(CFItem):
+    pass
+
+
+class CFTxOutCondSubtypeSrvStakeLock(CFItem):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.unlock = self._origin_item.timeUnlock
+        self.value = self._origin_item.value.coins
+        self.reinvest_percent = self._origin_item.reinvestPercent
+
+
+class CFTxOutCondSubtypeSrvStakePosDelegate(CFItem):
+    pass
\ No newline at end of file
diff --git a/mappings.py b/node/mappings.py
similarity index 82%
rename from mappings.py
rename to node/mappings.py
index e71fddd0484552c8aeebe6a8f890c98d7c0b6dc7..e747960fcfa239c65fb80bcb0c7ae69926e5dd52 100644
--- a/mappings.py
+++ b/node/mappings.py
@@ -4,19 +4,15 @@ from CellFrame.Common import (
     TxOutCondSubtypeSrvPay, TxOutCondSubtypeSrvXchange, TxOutCondSubtypeSrvStakeLock,
     TxOutCondSubtypeSrvStakePosDelegate
 )
-from CellFrame.Common import Datum
-from logger import logIt
 
-from shared.types import ItemTypes, DatumTypes
-from .cfnet_struct import (
+from ..common.types import ItemTypes, DatumTypes
+from .items import (
     CFItem, CFTxIn, CFTxInCond, CFTxOut, CFTxOutCond, CFTxPkey, CFTxSig, CFTxToken, CFTxReceipt, CFTxOutExt, CFTxTSD,
     CFTxOutCondSubtypeSrvPay, CFTxOutCondSubtypeSrvXchange, CFTxOutCondSubtypeSrvStakeLock,
     CFTxOutCondSubtypeSrvStakePosDelegate
 )
 
-from .cfnet_struct import CFDatumTX, CFDatumToken, CFDatumEmission, CFDatumCustom, CFDatumDecree, CFDatumAnchor
-
-# CFItemTypes = CFTxIn | CFTxInCond | CFTxOut | CFTxOutCond | CFTxPkey | CFTxSig | CFTxToken | CFTxReceipt | CFTxOutExt | CFTxTSD
+from .datums import CFDatumTX, CFDatumToken, CFDatumEmission, CFDatumCustom, CFDatumDecree, CFDatumAnchor
 
 ItemMeta = namedtuple('ItemMapping', ['cf_wrapper_cls', 'type'])
 
@@ -32,9 +28,12 @@ ITEM_WRAPPER_MAPPING = {
     TxOutExt: ItemMeta(CFTxOutExt, ItemTypes.TX_ITEM_TYPE_OUT_EXT),
     TxTSD: ItemMeta(CFTxTSD, ItemTypes.TX_ITEM_TYPE_TSD),
     TxOutCondSubtypeSrvPay: ItemMeta(CFTxOutCondSubtypeSrvPay, ItemTypes.DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY),
-    TxOutCondSubtypeSrvXchange: ItemMeta(CFTxOutCondSubtypeSrvXchange, ItemTypes.DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_XCHANGE),
-    TxOutCondSubtypeSrvStakeLock: ItemMeta(CFTxOutCondSubtypeSrvStakeLock, ItemTypes.DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK),
-    TxOutCondSubtypeSrvStakePosDelegate: ItemMeta(CFTxOutCondSubtypeSrvStakePosDelegate, ItemTypes.DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE),
+    TxOutCondSubtypeSrvXchange: ItemMeta(CFTxOutCondSubtypeSrvXchange,
+                                         ItemTypes.DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_XCHANGE),
+    TxOutCondSubtypeSrvStakeLock: ItemMeta(CFTxOutCondSubtypeSrvStakeLock,
+                                           ItemTypes.DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK),
+    TxOutCondSubtypeSrvStakePosDelegate: ItemMeta(CFTxOutCondSubtypeSrvStakePosDelegate,
+                                                  ItemTypes.DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE),
 }
 
 
diff --git a/node/net.py b/node/net.py
new file mode 100644
index 0000000000000000000000000000000000000000..b3a4058dfda1a4c2e822116086f9e4607ff89c59
--- /dev/null
+++ b/node/net.py
@@ -0,0 +1,142 @@
+from typing import Iterator
+
+import traceback
+
+from DAP.Core import logIt
+from DAP.Crypto import HashFast
+from CellFrame.Network import Net
+from CellFrame.Chain import ChainAtomPtr, Ledger, ChainAddr
+
+from .consensus import CFEvent, CFBlock
+from .datums import CFDatum, CFDatumTX
+from .types import CFNetState, ticker
+from ..common.types import ChainTypes
+
+
+class CFNet:
+    def __init__(self, name: str):
+        self.name = name
+        self._origin_net = Net.byName(name)
+        if not self._origin_net:
+            raise RuntimeError(f"No such net: {name}")
+        self.main = CFChain(self, 'main')
+        self.zerochain = CFChain(self, 'zerochain')
+
+    @property
+    def id(self) -> int:
+        return self._origin_net.id.long()
+
+    @property
+    def chains(self) -> list['CFChain']:
+        return [self.main, self.zerochain]
+
+    def get_ledger(self):
+        return CFLedger(self, self._origin_net.getLedger())
+
+    def register_gdbsync_notification_callback(self, callback, *args, **kwargs):
+        def callback_wrapper(op_code, group, key, value, *other):
+            callback(self, op_code, group, key, value, *args, net=self, **kwargs)
+
+        self._origin_net.addNotify(callback_wrapper, ())
+
+    def change_state(self, state: CFNetState):
+        if state == CFNetState.NET_STATE_OFFLINE:
+            self._origin_net.stop()
+        elif state == CFNetState.NET_STATE_ONLINE:
+            self._origin_net.start()
+        else:
+            raise NotImplemented("This state not implemented")
+
+
+class CFChain:
+    def __init__(self, net: CFNet, chain_name: str):
+        self.net = net
+        self._origin_chain = net._origin_net.getChainByName(chain_name)
+        if not self._origin_chain:
+            raise RuntimeError(f"chain with name={chain_name} not found in net with name={net.name}")
+        self.type = self._origin_chain.getCSName()
+        self.name = chain_name
+
+    def get_atoms(self) -> Iterator[CFBlock | CFEvent]:
+        iterator = self._origin_chain.createAtomIter(False)
+        ptr = self._origin_chain.atomIterGetFirst(iterator)
+
+        if not ptr:
+            logIt.message("not ptr")
+            return []
+
+        atom, size = ptr
+        while atom:
+            if size <= 0:
+                atom, size = self._origin_chain.atomIterGetNext(iterator)
+                continue
+            if self.type == ChainTypes.esbocs:
+                yield CFBlock(atom=atom, chain=self)
+            elif self.type == ChainTypes.dag_poa:
+                yield CFEvent(atom=atom, chain=self)
+            else:
+                raise TypeError(f"Invalid Chain type={self.type}")
+
+            atom, size = self._origin_chain.atomIterGetNext(iterator)
+
+    def get_datums(self) -> Iterator[CFDatum]:
+        for atom in self.get_atoms():
+            for datum in atom.get_datums():
+                yield datum
+
+    def get_transactions(self) -> Iterator[CFDatumTX]:
+        for datum in self.get_datums():
+            if datum._origin_datum.isDatumTX():
+                yield datum.get_sub_datum()
+
+    def register_mempool_notification_callback(self, callback, *args, **kwargs):
+        def callback_wrapper(op_code, group, key, value, *other):
+            logIt.message(f"{other=}")
+            try:
+                callback(op_code, group, key, value, *args, chain=self, **kwargs)
+            except Exception:
+                logIt.error(f"Error = {traceback.format_exc()}")
+
+        try:
+            self._origin_chain.addMempoolNotify(callback_wrapper, ())
+        except Exception:
+            logIt.error(f"Error = {traceback.format_exc()}")
+
+    def register_atom_notification_callback(self, callback, *args, **kwargs):
+        def callback_wrapper(atom: ChainAtomPtr, size, *other):
+            try:
+                if self.type == ChainTypes.esbocs:
+                    cf_atom = CFBlock(atom, self)
+                elif self.type == ChainTypes.dag_poa:
+                    cf_atom = CFEvent(atom, self)
+                else:
+                    raise TypeError(f"Invalid Chain type={self.type}")
+                callback(cf_atom, size, *args, chain=self, **kwargs)
+            except Exception:
+                logIt.error(traceback.format_exc())
+
+        self._origin_chain.addAtomNotify(callback_wrapper, ())
+
+
+class CFLedger:
+    def __init__(self, net: CFNet, ledger: Ledger = None):
+        self.net = net
+        self._origin_ledger = ledger or self.net.get_ledger()
+
+    def get_tx_ticker(self, datum: CFDatumTX) -> ticker | None:
+        return self._origin_ledger.txGetTokenTickerByHash(HashFast.fromString(str(datum.hash)))
+
+    def calc_address_balances(self, address: str) -> dict[ticker, str] | dict:
+        res = {}
+        chain_addr = ChainAddr.fromStr(address)
+        tickers = self._origin_ledger.addrGetTokenTickerAll(chain_addr)
+        for ticker in tickers:
+            balance = str(self._origin_ledger.calcBalance(chain_addr, ticker))
+            res[ticker] = balance
+        return res
+
+    def register_ledger_tx_notification_callback(self, callback, *args, **kwargs):
+        def callback_wrapper(ledger, tx, *other):
+            callback(ledger, tx, *args, net=self.net, **kwargs)
+
+        self._origin_ledger.txAddNotify(callback_wrapper, (args, kwargs))
diff --git a/node/notificators.py b/node/notificators.py
new file mode 100644
index 0000000000000000000000000000000000000000..9f8adc2434864ac53c1586f79a793a399dd7fe4f
--- /dev/null
+++ b/node/notificators.py
@@ -0,0 +1,73 @@
+from typing import Literal, Callable
+
+from .net import CFNet
+
+
+class CFNets:
+    def __init__(self, nets: list[CFNet]):
+        self.nets = nets
+
+    def mempool_notificator(self, *args, chain_name: Literal["main", "zerochain", "all"] = "all", **kwargs):
+        """
+        Usage example:
+        @CFNets(NETS).mempool_notificator("to_args", chain_name="main", key="to_kwargs")
+        def on_mempool_change(op_code, group, key, value, *args, chain:CFChain, **kwargs):
+            pass
+        """
+
+        def wrapper(callback: Callable[[], None]):
+            for net in self.nets:
+                if chain_name == "all":
+                    for chain in net.chains:
+                        chain.register_mempool_notification_callback(callback, *args, **kwargs)
+                else:
+                    getattr(net, chain_name).register_mempool_notification_callback(callback, *args, **kwargs)
+
+        return wrapper
+
+    def atom_notificator(self, *args, chain_name: Literal["main", "zerochain", "all"] = "all", **kwargs):
+        """
+        Usage example:
+        @CFNets(NETS).atom_notificator("to_args", chain_name="main", key="to_kwargs")
+        def on_new_atom(atom: CFBlock | CFEvent, size: int, *args, chain: CFChain, **kwargs):
+            pass
+        """
+
+        def wrapper(callback: Callable[[], None]):
+            for net in self.nets:
+                if chain_name == "all":
+                    for chain in net.chains:
+                        chain.register_atom_notification_callback(callback, *args, **kwargs)
+                else:
+                    getattr(net, chain_name).register_atom_notification_callback(callback, *args, **kwargs)
+
+        return wrapper
+
+    def gdbsync_notificator(self, *args, **kwargs):
+        """
+        Usage example:
+        @CFNets(NETS).gdbsync_notificator("to_args", key="to_kwargs")
+        def on_new_table_record(op_code, group, key, value, *args, net:CFNet, **kwargs):
+            pass
+        """
+
+        def wrapper(callback: Callable[[], None]):
+            for net in self.nets:
+                net.register_gdbsync_notification_callback(callback, *args, **kwargs)
+
+        return wrapper
+
+    def ledger_tx_notificator(self, *args, **kwargs):
+        """
+        Usage example:
+        @CFNets(NETS).ledger_tx_notificator("to_args", key="to_kwargs")
+        def on_new_ledger_transaction(ledger, tx, *args, net: CFNet, **kwargs):
+            pass
+        """
+
+        def wrapper(callback: Callable[[], None]):
+            for net in self.nets:
+                ledger = net.get_ledger()
+                ledger.register_ledger_tx_notification_callback(callback, *args, **kwargs)
+
+        return wrapper
diff --git a/types.py b/node/types.py
similarity index 63%
rename from types.py
rename to node/types.py
index 7674594638d2a24f4cbce089729eff73cc23556c..ad789be84e0c6a8b591fcc85daa2aeb1eae00ab5 100644
--- a/types.py
+++ b/node/types.py
@@ -1,9 +1,14 @@
+from typing import NewType
 from enum import Enum
-from shared.types import MixinHasValue
-from CellFrame.Network import NetState
 
+from CellFrame.Common import TxIn, TxInCond, TxOut, TxOutCond, TxPkey, TxSig, TxToken, TxReceipt, TxOutExt, TxTSD, \
+    TxOutCondSubtypeSrvStakeLock
 
-class CFNetState(MixinHasValue, str, Enum):
+OriginItemTypes = TxIn | TxInCond | TxOut | TxOutCond | TxPkey | TxSig | TxToken | TxReceipt | TxOutExt | TxTSD | TxOutCondSubtypeSrvStakeLock
+ticker = NewType("ticker", str)
+
+
+class CFNetState(str, Enum):
     NET_STATE_OFFLINE = "NET_STATE_OFFLINE"
     NET_STATE_ONLINE = "NET_STATE_ONLINE"