Repository: ergoplatform/sigma-rust (language: rust)

Issue:
Export SType



Discussion / hints:

Fix patch:
diff --git a/bindings/ergo-lib-wasm/src/ast.rs b/bindings/ergo-lib-wasm/src/ast.rs
index 7daf190b3..8cb0dc433 100644
--- a/bindings/ergo-lib-wasm/src/ast.rs
+++ b/bindings/ergo-lib-wasm/src/ast.rs
@@ -5,13 +5,14 @@ use std::convert::TryFrom;
 use js_sys::Uint8Array;
 use wasm_bindgen::prelude::*;
 
+use ergo_lib::ast::constant::TryExtractFrom;
+
 use crate::utils::I64;
-use ergo_lib::ast::TryExtractFrom;
 
 /// Ergo constant(evaluated) values
 #[wasm_bindgen]
 #[derive(PartialEq, Eq, Debug, Clone)]
-pub struct Constant(ergo_lib::ast::Constant);
+pub struct Constant(ergo_lib::ast::constant::Constant);
 
 #[wasm_bindgen]
 impl Constant {
@@ -24,7 +25,7 @@ impl Constant {
                     base16_bytes_str.clone()
                 ))
             })?;
-        ergo_lib::ast::Constant::try_from(bytes)
+        ergo_lib::ast::constant::Constant::try_from(bytes)
             .map_err(|e| JsValue::from_str(&format! {"{:?}", e}))
             .map(Constant)
     }
@@ -69,13 +70,13 @@ impl Constant {
     }
 }
 
-impl From<ergo_lib::ast::Constant> for Constant {
-    fn from(c: ergo_lib::ast::Constant) -> Self {
+impl From<ergo_lib::ast::constant::Constant> for Constant {
+    fn from(c: ergo_lib::ast::constant::Constant) -> Self {
         Constant(c)
     }
 }
 
-impl From<Constant> for ergo_lib::ast::Constant {
+impl From<Constant> for ergo_lib::ast::constant::Constant {
     fn from(c: Constant) -> Self {
         c.0
     }
diff --git a/ergo-lib/CHANGELOG.md b/ergo-lib/CHANGELOG.md
index 89627e59d..3d4c0adab 100644
--- a/ergo-lib/CHANGELOG.md
+++ b/ergo-lib/CHANGELOG.md
@@ -16,9 +16,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 
 ### Changed
 
-- Moved and changed visibility of various modules(input, data_input, prover_result, etc.) [#135](https://github.com/ergoplatform/sigma-rust/pull/135)
-- Added Context parameter to Prover::prove, Verifier::verify [#139](https://github.com/ergoplatform/sigma-rust/pull/139)
-- Moved all transaction-related parameters into TransactionContext parameter in Wallet::sign_transaction [#139](https://github.com/ergoplatform/sigma-rust/pull/139)
+- Move and changed visibility of various modules(input, data_input, prover_result, etc.) [#135](https://github.com/ergoplatform/sigma-rust/pull/135)
+- Add Context parameter to Prover::prove, Verifier::verify [#139](https://github.com/ergoplatform/sigma-rust/pull/139)
+- Move all transaction-related parameters into TransactionContext parameter in Wallet::sign_transaction [#139](https://github.com/ergoplatform/sigma-rust/pull/139)
+- Move Constant export from crate root to constant module (ast::constant) and made eval module private [#142](https://github.com/ergoplatform/sigma-rust/pull/142)
+- Make SType public [#142](https://github.com/ergoplatform/sigma-rust/pull/142)
 
 ## [0.3.0] - 2020-11-04
 
diff --git a/ergo-lib/src/ast.rs b/ergo-lib/src/ast.rs
index 97f47c497..7f9cfc8d0 100644
--- a/ergo-lib/src/ast.rs
+++ b/ergo-lib/src/ast.rs
@@ -1,176 +1,11 @@
 //! AST for ErgoTree
-use crate::eval::cost_accum::CostAccumulator;
-use crate::eval::EvalError;
-use crate::eval::Evaluable;
-use crate::{serialization::op_code::OpCode, types::*};
-use core::fmt;
-use Expr::*;
 
-mod constant;
-pub mod ops;
+pub(crate) mod box_methods;
+pub(crate) mod coll_methods;
+pub(crate) mod context_methods;
+pub(crate) mod expr;
+pub(crate) mod ops;
+pub(crate) mod predef_func;
+pub(crate) mod value;
 
-pub use constant::*;
-
-#[derive(PartialEq, Eq, Debug, Clone)]
-/// newtype for box register id
-pub struct RegisterId(u8);
-
-#[derive(PartialEq, Eq, Debug, Clone)]
-/// Expression in ErgoTree
-pub enum Expr {
-    /// Constant value
-    Const(Constant),
-    /// Placeholder for a constant
-    ConstPlaceholder(ConstantPlaceholder),
-    /// Collection of values (same type)
-    Coll {
-        /// Collection type
-        tpe: SType,
-        /// Values of the collection
-        v: Vec<Expr>,
-    },
-    /// Tuple
-    Tup {
-        /// Tuple type
-        tpe: SType,
-        /// Values of the tuple
-        v: Vec<Expr>,
-    },
-    /// Predefined functions (global)
-    PredefFunc(PredefFunc),
-    /// Collection type methods
-    CollM(CollMethods),
-    /// Box methods
-    BoxM(BoxMethods),
-    /// Context methods (i.e CONTEXT.INPUTS)
-    CtxM(ContextMethods),
-    /// Method call
-    MethodCall {
-        /// Method call type
-        tpe: SType,
-        /// Method call object
-        obj: Box<Expr>,
-        /// Method signature
-        method: SMethod,
-        /// Arguments of the method call
-        args: Vec<Expr>,
-    },
-    /// Binary operation
-    BinOp(ops::BinOp, Box<Expr>, Box<Expr>),
-}
-
-impl Expr {
-    /// Code (used in serialization)
-    pub fn op_code(&self) -> OpCode {
-        match self {
-            Const(_) => todo!(),
-            ConstPlaceholder(cp) => cp.op_code(),
-            _ => todo!(),
-        }
-    }
-
-    /// Type of the expression
-    pub fn tpe(&self) -> &SType {
-        match self {
-            Const(c) => &c.tpe,
-            _ => todo!(),
-        }
-    }
-}
-
-impl fmt::Display for Expr {
-    fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
-        todo!()
-    }
-}
-
-impl From<Constant> for Expr {
-    fn from(c: Constant) -> Self {
-        Self::Const(c)
-    }
-}
-
-#[derive(PartialEq, Eq, Debug, Clone)]
-/// Methods for Collection type instance
-pub enum CollMethods {
-    /// Fold method
-    Fold {
-        /// Collection
-        input: Box<Expr>,
-        /// Initial value for accumulator
-        zero: Box<Expr>,
-        /// Function (lambda)
-        fold_op: Box<Expr>,
-    },
-}
-
-#[derive(PartialEq, Eq, Debug, Clone)]
-/// Methods for Box type instance
-pub enum BoxMethods {
-    /// Box.RX methods
-    ExtractRegisterAs {
-        /// Box
-        input: Box<Expr>,
-        /// Register id to extract value from
-        register_id: RegisterId,
-    },
-}
-
-impl BoxMethods {
-    /// Code (serialization)
-    pub fn op_code(&self) -> OpCode {
-        todo!()
-    }
-}
-
-#[derive(PartialEq, Eq, Debug, Clone)]
-/// Methods for Context type instance
-pub enum ContextMethods {
-    /// Tx inputs
-    Inputs,
-    /// Tx outputs
-    Outputs,
-    /// Current blockchain height
-    Height,
-}
-
-impl Evaluable for ContextMethods {
-    fn eval(
-        &self,
-        _env: &crate::eval::Env,
-        _ca: &mut CostAccumulator,
-        ctx: &crate::eval::context::Context,
-    ) -> Result<Constant, EvalError> {
-        match self {
-            ContextMethods::Height => Ok(ctx.height.clone()),
-            _ => Err(EvalError::UnexpectedExpr),
-        }
-    }
-}
-
-#[derive(PartialEq, Eq, Debug, Clone)]
-/// Predefined (global) functions
-pub enum PredefFunc {
-    /// SHA256
-    Sha256 {
-        /// Byte array
-        input: Box<Expr>,
-    },
-}
-
-#[cfg(test)]
-mod tests {
-    #![allow(unused_imports)]
-    use super::*;
-    use crate::sigma_protocol::sigma_boolean::SigmaProp;
-    use proptest::prelude::*;
-
-    impl Arbitrary for Expr {
-        type Parameters = ();
-        type Strategy = BoxedStrategy<Self>;
-
-        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
-            prop_oneof![any::<Constant>().prop_map(Expr::Const)].boxed()
-        }
-    }
-}
+pub mod constant;
diff --git a/ergo-lib/src/ast/box_methods.rs b/ergo-lib/src/ast/box_methods.rs
new file mode 100644
index 000000000..3ff9e99e6
--- /dev/null
+++ b/ergo-lib/src/ast/box_methods.rs
@@ -0,0 +1,26 @@
+use crate::serialization::op_code::OpCode;
+
+use super::expr::Expr;
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+/// newtype for box register id
+pub struct RegisterId(u8); // should be a sum of NonMandatoryRegisterId and MandatoryRegisterId
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+/// Methods for Box type instance
+pub enum BoxM {
+    /// Box.RX methods
+    ExtractRegisterAs {
+        /// Box
+        input: Box<Expr>,
+        /// Register id to extract value from
+        register_id: RegisterId,
+    },
+}
+
+impl BoxM {
+    /// Code (serialization)
+    pub fn op_code(&self) -> OpCode {
+        todo!()
+    }
+}
diff --git a/ergo-lib/src/ast/coll_methods.rs b/ergo-lib/src/ast/coll_methods.rs
new file mode 100644
index 000000000..8fba9390b
--- /dev/null
+++ b/ergo-lib/src/ast/coll_methods.rs
@@ -0,0 +1,15 @@
+use super::expr::Expr;
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+/// Methods for Collection type instance
+pub enum CollM {
+    /// Fold method
+    Fold {
+        /// Collection
+        input: Box<Expr>,
+        /// Initial value for accumulator
+        zero: Box<Expr>,
+        /// Function (lambda)
+        fold_op: Box<Expr>,
+    },
+}
diff --git a/ergo-lib/src/ast/constant.rs b/ergo-lib/src/ast/constant.rs
index 1558a65f5..136e4bbe7 100644
--- a/ergo-lib/src/ast/constant.rs
+++ b/ergo-lib/src/ast/constant.rs
@@ -1,12 +1,11 @@
+//! Constant(Literal) IR node
+
 use crate::chain::{Base16DecodedBytes, Base16EncodedBytes};
-use crate::sigma_protocol::sigma_boolean::ProveDlog;
-use crate::sigma_protocol::sigma_boolean::SigmaBoolean;
-use crate::sigma_protocol::sigma_boolean::SigmaProofOfKnowledgeTree;
+use crate::types::stype::LiftIntoSType;
+use crate::types::stype::SType;
 use crate::{
-    chain::ergo_box::ErgoBox,
     serialization::{SerializationError, SigmaSerializable},
     sigma_protocol::{dlog_group::EcPoint, sigma_boolean::SigmaProp},
-    types::{LiftIntoSType, SType},
 };
 #[cfg(feature = "json")]
 use serde::{Deserialize, Serialize};
@@ -14,83 +13,12 @@ use std::convert::TryFrom;
 
 mod constant_placeholder;
 
-pub use constant_placeholder::*;
-
-#[derive(PartialEq, Eq, Debug, Clone)]
-/// Collection for primitive values (i.e byte array)
-pub enum CollPrim {
-    /// Collection of bytes
-    CollByte(Vec<i8>),
-}
-
-impl CollPrim {
-    /// Collection element type
-    pub fn elem_tpe(&self) -> &SType {
-        match self {
-            CollPrim::CollByte(_) => &SType::SByte,
-        }
-    }
-}
-
-/// Collection elements
-#[derive(PartialEq, Eq, Debug, Clone)]
-pub enum ConstantColl {
-    /// Collection elements stored as a vector of primitive types
-    Primitive(CollPrim),
-    /// Collection elements stored as a vector of ConstantVals
-    NonPrimitive {
-        /// Collection element type
-        elem_tpe: SType,
-        /// Collection elements
-        v: Vec<ConstantVal>,
-    },
-}
-
-impl ConstantColl {
-    /// Collection element type
-    pub fn elem_tpe(&self) -> &SType {
-        match self {
-            cp @ ConstantColl::Primitive(_) => cp.elem_tpe(),
-            ConstantColl::NonPrimitive { elem_tpe, .. } => elem_tpe,
-        }
-    }
-}
-
-/// Constant value
-#[derive(PartialEq, Eq, Debug, Clone)]
-pub enum ConstantVal {
-    /// Boolean
-    Boolean(bool),
-    /// Byte
-    Byte(i8),
-    /// Short
-    Short(i16),
-    /// Int
-    Int(i32),
-    /// Long
-    Long(i64),
-    /// Big integer
-    BigInt,
-    /// GroupElement
-    GroupElement(Box<EcPoint>),
-    /// Sigma property
-    SigmaProp(Box<SigmaProp>),
-    /// Box
-    CBox(Box<ErgoBox>),
-    /// AVL tree
-    AvlTree,
-    /// Collection of values of the same type
-    Coll(ConstantColl),
-    /// Tuple (arbitrary type values)
-    Tup(Vec<ConstantVal>),
-}
+pub(crate) use constant_placeholder::*;
 
-impl ConstantVal {
-    /// Create Sigma property constant
-    pub fn sigma_prop(prop: SigmaProp) -> ConstantVal {
-        ConstantVal::SigmaProp(Box::new(prop))
-    }
-}
+use super::value::Coll;
+use super::value::CollPrim;
+use super::value::StoredNonPrimitive;
+use super::value::Value;
 
 #[derive(PartialEq, Eq, Debug, Clone)]
 #[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
@@ -103,7 +31,7 @@ pub struct Constant {
     /// Constant type
     pub tpe: SType,
     /// Constant value
-    pub v: ConstantVal,
+    pub v: Value,
 }
 
 impl From<Constant> for Base16EncodedBytes {
@@ -127,12 +55,6 @@ impl Constant {
     }
 }
 
-impl Into<ConstantVal> for bool {
-    fn into(self) -> ConstantVal {
-        ConstantVal::Boolean(self)
-    }
-}
-
 impl From<bool> for Constant {
     fn from(v: bool) -> Constant {
         Constant {
@@ -142,12 +64,6 @@ impl From<bool> for Constant {
     }
 }
 
-impl Into<ConstantVal> for i8 {
-    fn into(self) -> ConstantVal {
-        ConstantVal::Byte(self)
-    }
-}
-
 impl From<i8> for Constant {
     fn from(v: i8) -> Constant {
         Constant {
@@ -157,12 +73,6 @@ impl From<i8> for Constant {
     }
 }
 
-impl Into<ConstantVal> for i16 {
-    fn into(self) -> ConstantVal {
-        ConstantVal::Short(self)
-    }
-}
-
 impl From<i16> for Constant {
     fn from(v: i16) -> Constant {
         Constant {
@@ -172,12 +82,6 @@ impl From<i16> for Constant {
     }
 }
 
-impl Into<ConstantVal> for i32 {
-    fn into(self) -> ConstantVal {
-        ConstantVal::Int(self)
-    }
-}
-
 impl From<i32> for Constant {
     fn from(v: i32) -> Constant {
         Constant {
@@ -187,12 +91,6 @@ impl From<i32> for Constant {
     }
 }
 
-impl Into<ConstantVal> for i64 {
-    fn into(self) -> ConstantVal {
-        ConstantVal::Long(self)
-    }
-}
-
 impl From<i64> for Constant {
     fn from(v: i64) -> Constant {
         Constant {
@@ -202,12 +100,6 @@ impl From<i64> for Constant {
     }
 }
 
-impl<T: Into<SigmaProp>> From<T> for ConstantVal {
-    fn from(t: T) -> Self {
-        ConstantVal::SigmaProp(Box::new(t.into()))
-    }
-}
-
 impl From<SigmaProp> for Constant {
     fn from(v: SigmaProp) -> Constant {
         Constant {
@@ -217,12 +109,6 @@ impl From<SigmaProp> for Constant {
     }
 }
 
-impl Into<ConstantVal> for EcPoint {
-    fn into(self) -> ConstantVal {
-        ConstantVal::GroupElement(Box::new(self))
-    }
-}
-
 impl From<EcPoint> for Constant {
     fn from(v: EcPoint) -> Constant {
         Constant {
@@ -232,37 +118,11 @@ impl From<EcPoint> for Constant {
     }
 }
 
-/// Marker trait to select types for which CollElems::NonPrimitive is used to store elements as Vec<ConstantVal>
-pub trait StoredNonPrimitive {}
-
-impl StoredNonPrimitive for bool {}
-impl StoredNonPrimitive for i16 {}
-impl StoredNonPrimitive for i32 {}
-impl StoredNonPrimitive for i64 {}
-
-impl<T: LiftIntoSType + StoredNonPrimitive + Into<ConstantVal>> Into<ConstantVal> for Vec<T> {
-    fn into(self) -> ConstantVal {
-        ConstantVal::Coll(ConstantColl::NonPrimitive {
-            elem_tpe: T::stype(),
-            v: self.into_iter().map(|i| i.into()).collect(),
-        })
-    }
-}
-
-impl<T: LiftIntoSType + StoredNonPrimitive + Into<ConstantVal>> From<Vec<T>> for Constant {
-    fn from(v: Vec<T>) -> Self {
-        Constant {
-            tpe: Vec::<T>::stype(),
-            v: v.into(),
-        }
-    }
-}
-
 impl From<Vec<u8>> for Constant {
     fn from(v: Vec<u8>) -> Self {
         Constant {
             tpe: SType::SColl(Box::new(SType::SByte)),
-            v: ConstantVal::Coll(ConstantColl::Primitive(CollPrim::CollByte(
+            v: Value::Coll(Coll::Primitive(CollPrim::CollByte(
                 v.into_iter().map(|b| b as i8).collect(),
             ))),
         }
@@ -273,7 +133,16 @@ impl From<Vec<i8>> for Constant {
     fn from(v: Vec<i8>) -> Constant {
         Constant {
             tpe: SType::SColl(Box::new(SType::SByte)),
-            v: ConstantVal::Coll(ConstantColl::Primitive(CollPrim::CollByte(v))),
+            v: Value::Coll(Coll::Primitive(CollPrim::CollByte(v))),
+        }
+    }
+}
+
+impl<T: LiftIntoSType + StoredNonPrimitive + Into<Value>> From<Vec<T>> for Constant {
+    fn from(v: Vec<T>) -> Self {
+        Constant {
+            tpe: Vec::<T>::stype(),
+            v: v.into(),
         }
     }
 }
@@ -288,111 +157,18 @@ pub trait TryExtractFrom<T>: Sized {
     fn try_extract_from(c: T) -> Result<Self, TryExtractFromError>;
 }
 
-impl TryExtractFrom<ConstantVal> for bool {
-    fn try_extract_from(cv: ConstantVal) -> Result<bool, TryExtractFromError> {
-        match cv {
-            ConstantVal::Boolean(v) => Ok(v),
-            _ => Err(TryExtractFromError(format!(
-                "expected bool, found {:?}",
-                cv
-            ))),
-        }
-    }
-}
-
-impl TryExtractFrom<ConstantVal> for i8 {
-    fn try_extract_from(cv: ConstantVal) -> Result<i8, TryExtractFromError> {
-        match cv {
-            ConstantVal::Byte(v) => Ok(v),
-            _ => Err(TryExtractFromError(format!("expected i8, found {:?}", cv))),
-        }
-    }
-}
-
-impl TryExtractFrom<ConstantVal> for i16 {
-    fn try_extract_from(cv: ConstantVal) -> Result<i16, TryExtractFromError> {
-        match cv {
-            ConstantVal::Short(v) => Ok(v),
-            _ => Err(TryExtractFromError(format!("expected i16, found {:?}", cv))),
-        }
-    }
-}
-
-impl TryExtractFrom<ConstantVal> for i32 {
-    fn try_extract_from(cv: ConstantVal) -> Result<i32, TryExtractFromError> {
-        match cv {
-            ConstantVal::Int(v) => Ok(v),
-            _ => Err(TryExtractFromError(format!("expected i32, found {:?}", cv))),
-        }
-    }
-}
-
-impl TryExtractFrom<ConstantVal> for i64 {
-    fn try_extract_from(cv: ConstantVal) -> Result<i64, TryExtractFromError> {
-        match cv {
-            ConstantVal::Long(v) => Ok(v),
-            _ => Err(TryExtractFromError(format!("expected i64, found {:?}", cv))),
-        }
-    }
-}
-
-impl TryExtractFrom<ConstantVal> for EcPoint {
-    fn try_extract_from(cv: ConstantVal) -> Result<EcPoint, TryExtractFromError> {
-        match cv {
-            ConstantVal::GroupElement(v) => Ok(*v),
-            _ => Err(TryExtractFromError(format!(
-                "expected EcPoint, found {:?}",
-                cv
-            ))),
-        }
-    }
-}
-
-impl TryExtractFrom<ConstantVal> for SigmaProp {
-    fn try_extract_from(cv: ConstantVal) -> Result<SigmaProp, TryExtractFromError> {
-        match cv {
-            ConstantVal::SigmaProp(v) => Ok(*v),
-            _ => Err(TryExtractFromError(format!(
-                "expected SigmaProp, found {:?}",
-                cv
-            ))),
-        }
-    }
-}
-
-impl TryFrom<ConstantVal> for ProveDlog {
-    type Error = TryExtractFromError;
-    fn try_from(cv: ConstantVal) -> Result<Self, Self::Error> {
-        match cv {
-            ConstantVal::SigmaProp(sp) => match sp.value() {
-                SigmaBoolean::ProofOfKnowledge(SigmaProofOfKnowledgeTree::ProveDlog(
-                    prove_dlog,
-                )) => Ok(prove_dlog.clone()),
-                _ => Err(TryExtractFromError(format!(
-                    "expected ProveDlog, found {:?}",
-                    sp
-                ))),
-            },
-            _ => Err(TryExtractFromError(format!(
-                "expected SigmaProp, found {:?}",
-                cv
-            ))),
-        }
-    }
-}
-
-impl<T: TryExtractFrom<ConstantVal>> TryExtractFrom<Constant> for T {
+impl<T: TryExtractFrom<Value>> TryExtractFrom<Constant> for T {
     fn try_extract_from(cv: Constant) -> Result<Self, TryExtractFromError> {
         T::try_extract_from(cv.v)
     }
 }
 
-impl<T: TryExtractFrom<ConstantVal> + StoredNonPrimitive + LiftIntoSType> TryExtractFrom<Constant>
+impl<T: TryExtractFrom<Value> + StoredNonPrimitive + LiftIntoSType> TryExtractFrom<Constant>
     for Vec<T>
 {
     fn try_extract_from(c: Constant) -> Result<Self, TryExtractFromError> {
         match c.v {
-            ConstantVal::Coll(ConstantColl::NonPrimitive { elem_tpe: _, v }) => {
+            Value::Coll(Coll::NonPrimitive { elem_tpe: _, v }) => {
                 v.into_iter().map(T::try_extract_from).collect()
             }
             _ => Err(TryExtractFromError(format!(
@@ -407,7 +183,7 @@ impl<T: TryExtractFrom<ConstantVal> + StoredNonPrimitive + LiftIntoSType> TryExt
 impl TryExtractFrom<Constant> for Vec<i8> {
     fn try_extract_from(c: Constant) -> Result<Self, TryExtractFromError> {
         match c.v {
-            ConstantVal::Coll(ConstantColl::Primitive(CollPrim::CollByte(bs))) => Ok(bs),
+            Value::Coll(Coll::Primitive(CollPrim::CollByte(bs))) => Ok(bs),
             _ => Err(TryExtractFromError(format!(
                 "expected {:?}, found {:?}",
                 std::any::type_name::<Self>(),
diff --git a/ergo-lib/src/ast/constant/constant_placeholder.rs b/ergo-lib/src/ast/constant/constant_placeholder.rs
index ddbf364ea..0dbe3b391 100644
--- a/ergo-lib/src/ast/constant/constant_placeholder.rs
+++ b/ergo-lib/src/ast/constant/constant_placeholder.rs
@@ -1,5 +1,5 @@
 use crate::serialization::op_code::OpCode;
-use crate::types::SType;
+use crate::types::stype::SType;
 
 /// Placeholder for a constant in ErgoTree.
 #[derive(PartialEq, Eq, Debug, Clone)]
diff --git a/ergo-lib/src/ast/context_methods.rs b/ergo-lib/src/ast/context_methods.rs
new file mode 100644
index 000000000..cdf9bfd81
--- /dev/null
+++ b/ergo-lib/src/ast/context_methods.rs
@@ -0,0 +1,30 @@
+use crate::eval::cost_accum::CostAccumulator;
+use crate::eval::EvalError;
+use crate::eval::Evaluable;
+
+use super::constant::Constant;
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+/// Methods for Context type instance
+pub enum ContextM {
+    /// Tx inputs
+    Inputs,
+    /// Tx outputs
+    Outputs,
+    /// Current blockchain height
+    Height,
+}
+
+impl Evaluable for ContextM {
+    fn eval(
+        &self,
+        _env: &crate::eval::Env,
+        _ca: &mut CostAccumulator,
+        ctx: &crate::eval::context::Context,
+    ) -> Result<Constant, EvalError> {
+        match self {
+            ContextM::Height => Ok(ctx.height.clone()),
+            _ => Err(EvalError::UnexpectedExpr),
+        }
+    }
+}
diff --git a/ergo-lib/src/ast/expr.rs b/ergo-lib/src/ast/expr.rs
new file mode 100644
index 000000000..93bf639fe
--- /dev/null
+++ b/ergo-lib/src/ast/expr.rs
@@ -0,0 +1,105 @@
+use core::fmt;
+
+use crate::serialization::op_code::OpCode;
+use crate::types::smethod::SMethod;
+use crate::types::stype::SType;
+
+use super::box_methods::BoxM;
+use super::coll_methods::CollM;
+use super::constant::Constant;
+use super::constant::ConstantPlaceholder;
+use super::context_methods::ContextM;
+use super::ops;
+use super::predef_func::PredefFunc;
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+/// Expression in ErgoTree
+pub enum Expr {
+    /// Constant value
+    Const(Constant),
+    /// Placeholder for a constant
+    ConstPlaceholder(ConstantPlaceholder),
+    /// Collection of values (same type)
+    Coll {
+        /// Collection type
+        tpe: SType,
+        /// Values of the collection
+        v: Vec<Expr>,
+    },
+    /// Tuple
+    Tup {
+        /// Tuple type
+        tpe: SType,
+        /// Values of the tuple
+        v: Vec<Expr>,
+    },
+    /// Predefined functions (global)
+    PredefFunc(PredefFunc),
+    /// Collection type methods
+    CollM(CollM),
+    /// Box methods
+    BoxM(BoxM),
+    /// Context methods (i.e CONTEXT.INPUTS)
+    ContextM(ContextM),
+    /// Method call
+    MethodCall {
+        /// Method call type
+        tpe: SType,
+        /// Method call object
+        obj: Box<Expr>,
+        /// Method signature
+        method: SMethod,
+        /// Arguments of the method call
+        args: Vec<Expr>,
+    },
+    /// Binary operation
+    BinOp(ops::BinOp, Box<Expr>, Box<Expr>),
+}
+
+impl Expr {
+    /// Code (used in serialization)
+    pub fn op_code(&self) -> OpCode {
+        match self {
+            Expr::Const(_) => todo!(),
+            Expr::ConstPlaceholder(cp) => cp.op_code(),
+            _ => todo!(),
+        }
+    }
+
+    /// Type of the expression
+    pub fn tpe(&self) -> &SType {
+        match self {
+            Expr::Const(c) => &c.tpe,
+            _ => todo!(),
+        }
+    }
+}
+
+impl fmt::Display for Expr {
+    fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
+        todo!()
+    }
+}
+
+impl From<Constant> for Expr {
+    fn from(c: Constant) -> Self {
+        Self::Const(c)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    #![allow(unused_imports)]
+    use super::*;
+    use crate::sigma_protocol::sigma_boolean::SigmaProp;
+    use proptest::prelude::*;
+
+    impl Arbitrary for Expr {
+        type Parameters = ();
+        type Strategy = BoxedStrategy<Self>;
+
+        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
+            prop_oneof![any::<Constant>().prop_map(Expr::Const)].boxed()
+        }
+    }
+}
diff --git a/ergo-lib/src/ast/predef_func.rs b/ergo-lib/src/ast/predef_func.rs
new file mode 100644
index 000000000..c1dad77dc
--- /dev/null
+++ b/ergo-lib/src/ast/predef_func.rs
@@ -0,0 +1,11 @@
+use super::expr::Expr;
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+/// Predefined (global) functions
+pub enum PredefFunc {
+    /// SHA256
+    Sha256 {
+        /// Byte array
+        input: Box<Expr>,
+    },
+}
diff --git a/ergo-lib/src/ast/value.rs b/ergo-lib/src/ast/value.rs
new file mode 100644
index 000000000..1538c083a
--- /dev/null
+++ b/ergo-lib/src/ast/value.rs
@@ -0,0 +1,241 @@
+use std::convert::TryFrom;
+
+use crate::chain::ergo_box::ErgoBox;
+use crate::sigma_protocol::dlog_group::EcPoint;
+use crate::sigma_protocol::sigma_boolean::ProveDlog;
+use crate::sigma_protocol::sigma_boolean::SigmaBoolean;
+use crate::sigma_protocol::sigma_boolean::SigmaProofOfKnowledgeTree;
+use crate::sigma_protocol::sigma_boolean::SigmaProp;
+use crate::types::stype::LiftIntoSType;
+use crate::types::stype::SType;
+
+use super::constant::TryExtractFrom;
+use super::constant::TryExtractFromError;
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+/// Collection for primitive values (i.e byte array)
+pub enum CollPrim {
+    /// Collection of bytes
+    CollByte(Vec<i8>),
+}
+
+impl CollPrim {
+    /// Collection element type
+    pub fn elem_tpe(&self) -> &SType {
+        match self {
+            CollPrim::CollByte(_) => &SType::SByte,
+        }
+    }
+}
+
+/// Collection elements
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub enum Coll {
+    /// Collection elements stored as a vector of primitive types
+    Primitive(CollPrim),
+    /// Collection elements stored as a vector of ConstantVals
+    NonPrimitive {
+        /// Collection element type
+        elem_tpe: SType,
+        /// Collection elements
+        v: Vec<Value>,
+    },
+}
+
+impl Coll {
+    /// Collection element type
+    pub fn elem_tpe(&self) -> &SType {
+        match self {
+            cp @ Coll::Primitive(_) => cp.elem_tpe(),
+            Coll::NonPrimitive { elem_tpe, .. } => elem_tpe,
+        }
+    }
+}
+
+/// Constant value
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub enum Value {
+    /// Boolean
+    Boolean(bool),
+    /// Byte
+    Byte(i8),
+    /// Short
+    Short(i16),
+    /// Int
+    Int(i32),
+    /// Long
+    Long(i64),
+    /// Big integer
+    BigInt,
+    /// GroupElement
+    GroupElement(Box<EcPoint>),
+    /// Sigma property
+    SigmaProp(Box<SigmaProp>),
+    /// Box
+    CBox(Box<ErgoBox>),
+    /// AVL tree
+    AvlTree,
+    /// Collection of values of the same type
+    Coll(Coll),
+    /// Tuple (arbitrary type values)
+    Tup(Vec<Value>),
+}
+
+impl Value {
+    /// Create Sigma property constant
+    pub fn sigma_prop(prop: SigmaProp) -> Value {
+        Value::SigmaProp(Box::new(prop))
+    }
+}
+
+impl Into<Value> for bool {
+    fn into(self) -> Value {
+        Value::Boolean(self)
+    }
+}
+
+impl Into<Value> for i8 {
+    fn into(self) -> Value {
+        Value::Byte(self)
+    }
+}
+
+impl Into<Value> for i16 {
+    fn into(self) -> Value {
+        Value::Short(self)
+    }
+}
+
+impl Into<Value> for i32 {
+    fn into(self) -> Value {
+        Value::Int(self)
+    }
+}
+
+impl Into<Value> for i64 {
+    fn into(self) -> Value {
+        Value::Long(self)
+    }
+}
+
+impl<T: Into<SigmaProp>> From<T> for Value {
+    fn from(t: T) -> Self {
+        Value::SigmaProp(Box::new(t.into()))
+    }
+}
+
+impl Into<Value> for EcPoint {
+    fn into(self) -> Value {
+        Value::GroupElement(Box::new(self))
+    }
+}
+
+/// Marker trait to select types for which CollElems::NonPrimitive is used to store elements as Vec<ConstantVal>
+pub trait StoredNonPrimitive {}
+
+impl StoredNonPrimitive for bool {}
+impl StoredNonPrimitive for i16 {}
+impl StoredNonPrimitive for i32 {}
+impl StoredNonPrimitive for i64 {}
+
+impl<T: LiftIntoSType + StoredNonPrimitive + Into<Value>> Into<Value> for Vec<T> {
+    fn into(self) -> Value {
+        Value::Coll(Coll::NonPrimitive {
+            elem_tpe: T::stype(),
+            v: self.into_iter().map(|i| i.into()).collect(),
+        })
+    }
+}
+
+impl TryExtractFrom<Value> for bool {
+    fn try_extract_from(cv: Value) -> Result<bool, TryExtractFromError> {
+        match cv {
+            Value::Boolean(v) => Ok(v),
+            _ => Err(TryExtractFromError(format!(
+                "expected bool, found {:?}",
+                cv
+            ))),
+        }
+    }
+}
+
+impl TryExtractFrom<Value> for i8 {
+    fn try_extract_from(cv: Value) -> Result<i8, TryExtractFromError> {
+        match cv {
+            Value::Byte(v) => Ok(v),
+            _ => Err(TryExtractFromError(format!("expected i8, found {:?}", cv))),
+        }
+    }
+}
+
+impl TryExtractFrom<Value> for i16 {
+    fn try_extract_from(cv: Value) -> Result<i16, TryExtractFromError> {
+        match cv {
+            Value::Short(v) => Ok(v),
+            _ => Err(TryExtractFromError(format!("expected i16, found {:?}", cv))),
+        }
+    }
+}
+
+impl TryExtractFrom<Value> for i32 {
+    fn try_extract_from(cv: Value) -> Result<i32, TryExtractFromError> {
+        match cv {
+            Value::Int(v) => Ok(v),
+            _ => Err(TryExtractFromError(format!("expected i32, found {:?}", cv))),
+        }
+    }
+}
+
+impl TryExtractFrom<Value> for i64 {
+    fn try_extract_from(cv: Value) -> Result<i64, TryExtractFromError> {
+        match cv {
+            Value::Long(v) => Ok(v),
+            _ => Err(TryExtractFromError(format!("expected i64, found {:?}", cv))),
+        }
+    }
+}
+
+impl TryExtractFrom<Value> for EcPoint {
+    fn try_extract_from(cv: Value) -> Result<EcPoint, TryExtractFromError> {
+        match cv {
+            Value::GroupElement(v) => Ok(*v),
+            _ => Err(TryExtractFromError(format!(
+                "expected EcPoint, found {:?}",
+                cv
+            ))),
+        }
+    }
+}
+
+impl TryExtractFrom<Value> for SigmaProp {
+    fn try_extract_from(cv: Value) -> Result<SigmaProp, TryExtractFromError> {
+        match cv {
+            Value::SigmaProp(v) => Ok(*v),
+            _ => Err(TryExtractFromError(format!(
+                "expected SigmaProp, found {:?}",
+                cv
+            ))),
+        }
+    }
+}
+
+impl TryFrom<Value> for ProveDlog {
+    type Error = TryExtractFromError;
+    fn try_from(cv: Value) -> Result<Self, Self::Error> {
+        match cv {
+            Value::SigmaProp(sp) => match sp.value() {
+                SigmaBoolean::ProofOfKnowledge(SigmaProofOfKnowledgeTree::ProveDlog(
+                    prove_dlog,
+                )) => Ok(prove_dlog.clone()),
+                _ => Err(TryExtractFromError(format!(
+                    "expected ProveDlog, found {:?}",
+                    sp
+                ))),
+            },
+            _ => Err(TryExtractFromError(format!(
+                "expected SigmaProp, found {:?}",
+                cv
+            ))),
+        }
+    }
+}
diff --git a/ergo-lib/src/chain/address.rs b/ergo-lib/src/chain/address.rs
index e9c31a913..c2216095a 100644
--- a/ergo-lib/src/chain/address.rs
+++ b/ergo-lib/src/chain/address.rs
@@ -1,9 +1,10 @@
 //! Address types
 
 use super::digest32;
-use crate::types::SType;
+use crate::ast::constant::Constant;
+use crate::ast::expr::Expr;
+use crate::types::stype::SType;
 use crate::{
-    ast::{Constant, Expr},
     ergo_tree::{ErgoTree, ErgoTreeParsingError},
     serialization::{SerializationError, SigmaSerializable},
     sigma_protocol::{
diff --git a/ergo-lib/src/chain/ergo_box.rs b/ergo-lib/src/chain/ergo_box.rs
index 1aeec943a..36f326e9f 100644
--- a/ergo-lib/src/chain/ergo_box.rs
+++ b/ergo-lib/src/chain/ergo_box.rs
@@ -86,6 +86,9 @@ pub struct ErgoBox {
 }
 
 impl ErgoBox {
+    /// Maximum number of tokens in the box
+    pub const MAX_TOKENS_COUNT: usize = u8::MAX as usize;
+
     /// Crate new box
     pub fn new(
         value: BoxValue,
diff --git a/ergo-lib/src/chain/ergo_box/box_builder.rs b/ergo-lib/src/chain/ergo_box/box_builder.rs
index 0bc14ec58..c916b29b4 100644
--- a/ergo-lib/src/chain/ergo_box/box_builder.rs
+++ b/ergo-lib/src/chain/ergo_box/box_builder.rs
@@ -3,7 +3,7 @@
 use std::collections::HashMap;
 use std::convert::{TryFrom, TryInto};
 
-use crate::ast::Constant;
+use crate::ast::constant::Constant;
 use crate::chain::token::Token;
 use crate::ergo_tree::ErgoTree;
 use crate::serialization::SigmaSerializable;
diff --git a/ergo-lib/src/chain/ergo_box/register.rs b/ergo-lib/src/chain/ergo_box/register.rs
index da2d0ecf0..d95b4ed6c 100644
--- a/ergo-lib/src/chain/ergo_box/register.rs
+++ b/ergo-lib/src/chain/ergo_box/register.rs
@@ -1,6 +1,6 @@
 //! Box registers
 
-use crate::{ast::Constant, serialization::SerializationError};
+use crate::{ast::constant::Constant, serialization::SerializationError};
 #[cfg(feature = "json")]
 use serde::{Deserialize, Serialize};
 use std::{collections::HashMap, convert::TryFrom};
diff --git a/ergo-lib/src/chain/transaction.rs b/ergo-lib/src/chain/transaction.rs
index 6783a95d6..79a74fbca 100644
--- a/ergo-lib/src/chain/transaction.rs
+++ b/ergo-lib/src/chain/transaction.rs
@@ -94,6 +94,9 @@ pub struct Transaction {
 }
 
 impl Transaction {
+    /// Maximum number of outputs
+    pub const MAX_OUTPUTS_COUNT: usize = u16::MAX as usize;
+
     /// Creates new transaction
     pub fn new(
         inputs: Vec<Input>,
@@ -195,6 +198,11 @@ impl SigmaSerializable for Transaction {
 
         // parse distinct ids of tokens in transaction outputs
         let tokens_count = r.get_u32()?;
+        if tokens_count as usize > Transaction::MAX_OUTPUTS_COUNT * ErgoBox::MAX_TOKENS_COUNT {
+            return Err(SerializationError::ValueOutOfBounds(
+                "too many tokens in transaction".to_string(),
+            ));
+        }
         let mut token_ids = IndexSet::with_capacity(tokens_count as usize);
         for _ in 0..tokens_count {
             token_ids.insert(TokenId::sigma_parse(r)?);
diff --git a/ergo-lib/src/ergo_tree.rs b/ergo-lib/src/ergo_tree.rs
index f06f1befe..2a4344a4a 100644
--- a/ergo-lib/src/ergo_tree.rs
+++ b/ergo-lib/src/ergo_tree.rs
@@ -1,15 +1,14 @@
 //! ErgoTree
-use crate::ast::TryExtractFromError;
+use crate::ast::constant::Constant;
+use crate::ast::constant::TryExtractFromError;
+use crate::ast::expr::Expr;
 use crate::serialization::{
     sigma_byte_reader::{SigmaByteRead, SigmaByteReader},
     sigma_byte_writer::{SigmaByteWrite, SigmaByteWriter},
     SerializationError, SigmaSerializable,
 };
 use crate::sigma_protocol::sigma_boolean::ProveDlog;
-use crate::{
-    ast::{Constant, Expr},
-    types::SType,
-};
+use crate::types::stype::SType;
 use io::{Cursor, Read};
 
 use crate::serialization::constant_store::ConstantStore;
@@ -77,6 +76,9 @@ pub enum ErgoTreeParsingError {
 impl ErgoTree {
     const DEFAULT_HEADER: ErgoTreeHeader = ErgoTreeHeader(0);
 
+    /// Reasonable limit for the number of constants allowed in the ErgoTree
+    pub const MAX_CONSTANTS_COUNT: usize = 4096;
+
     /// get Expr out of ErgoTree
     pub fn proposition(&self) -> Result<Rc<Expr>, ErgoTreeParsingError> {
         let root = self
@@ -180,6 +182,11 @@ impl SigmaSerializable for ErgoTree {
         let header = ErgoTreeHeader::sigma_parse(r)?;
         let constants = if header.is_constant_segregation() {
             let constants_len = r.get_u32()?;
+            if constants_len as usize > ErgoTree::MAX_CONSTANTS_COUNT {
+                return Err(SerializationError::ValueOutOfBounds(
+                    "too many constants".to_string(),
+                ));
+            }
             let mut constants = Vec::with_capacity(constants_len as usize);
             for _ in 0..constants_len {
                 let c = Constant::sigma_parse(r)?;
@@ -206,6 +213,11 @@ impl SigmaSerializable for ErgoTree {
         let header = ErgoTreeHeader::sigma_parse(&mut r)?;
         let constants = if header.is_constant_segregation() {
             let constants_len = r.get_u32()?;
+            if constants_len as usize > ErgoTree::MAX_CONSTANTS_COUNT {
+                return Err(SerializationError::ValueOutOfBounds(
+                    "too many constants".to_string(),
+                ));
+            }
             let mut constants = Vec::with_capacity(constants_len as usize);
             for _ in 0..constants_len {
                 match Constant::sigma_parse(&mut r) {
@@ -280,10 +292,11 @@ impl TryFrom<ErgoTree> for ProveDlog {
 mod tests {
     #![allow(unused_imports)]
     use super::*;
+    use crate::ast::value::Value;
+    use crate::chain;
     use crate::chain::Base16DecodedBytes;
     use crate::serialization::sigma_serialize_roundtrip;
     use crate::sigma_protocol::sigma_boolean::SigmaProp;
-    use crate::{ast::ConstantVal, chain, types::SType};
     use proptest::prelude::*;
 
     impl Arbitrary for ErgoTree {
@@ -343,7 +356,7 @@ mod tests {
     fn test_constant_segregation() {
         let expr = Expr::Const(Constant {
             tpe: SType::SBoolean,
-            v: ConstantVal::Boolean(true),
+            v: Value::Boolean(true),
         });
         let ergo_tree = ErgoTree::with_segregation(Rc::new(expr.clone()));
         let bytes = ergo_tree.sigma_serialize_bytes();
diff --git a/ergo-lib/src/eval.rs b/ergo-lib/src/eval.rs
index 4f9d6381b..020c50ee2 100644
--- a/ergo-lib/src/eval.rs
+++ b/ergo-lib/src/eval.rs
@@ -1,9 +1,8 @@
 //! Interpreter
-use crate::{
-    ast::{Constant, ConstantVal, Expr},
-    sigma_protocol::sigma_boolean::SigmaBoolean,
-    types::SType,
-};
+use crate::ast::constant::Constant;
+use crate::ast::expr::Expr;
+use crate::ast::value::Value;
+use crate::{sigma_protocol::sigma_boolean::SigmaBoolean, types::stype::SType};
 
 use cost_accum::CostAccumulator;
 use thiserror::Error;
@@ -58,14 +57,14 @@ pub trait Evaluator {
             match v {
                 Constant {
                     tpe: SType::SBoolean,
-                    v: ConstantVal::Boolean(b),
+                    v: Value::Boolean(b),
                 } => Ok(ReductionResult {
                     sigma_prop: SigmaBoolean::TrivialProp(b),
                     cost: 0,
                 }),
                 Constant {
                     tpe: SType::SSigmaProp,
-                    v: ConstantVal::SigmaProp(sp),
+                    v: Value::SigmaProp(sp),
                 } => Ok(ReductionResult {
                     sigma_prop: sp.value().clone(),
                     cost: 0,
@@ -102,7 +101,7 @@ fn eval(
         Expr::PredefFunc(_) => todo!(),
         Expr::CollM(_) => todo!(),
         Expr::BoxM(_) => todo!(),
-        Expr::CtxM(v) => v.eval(env, ca, ctx),
+        Expr::ContextM(v) => v.eval(env, ca, ctx),
         Expr::MethodCall { .. } => todo!(),
         Expr::BinOp(_bin_op, l, r) => {
             let _v_l = eval(l, env, ca, ctx)?;
@@ -121,14 +120,15 @@ fn eval(
 
 #[cfg(test)]
 mod tests {
-    use crate::ast::ContextMethods;
-    use crate::ast::TryExtractFrom;
+
+    use crate::ast::constant::TryExtractFrom;
+    use crate::ast::context_methods::ContextM;
 
     use super::*;
 
     #[test]
     fn height() {
-        let expr = Expr::CtxM(ContextMethods::Height);
+        let expr = Expr::ContextM(ContextM::Height);
         let mut ca = CostAccumulator::new(0, None);
         let res = eval(&expr, &Env::empty(), &mut ca, &Context::dummy()).unwrap();
         assert_eq!(i32::try_extract_from(res).unwrap(), 0);
diff --git a/ergo-lib/src/eval/context.rs b/ergo-lib/src/eval/context.rs
index f4d79ec69..76ebc24ae 100644
--- a/ergo-lib/src/eval/context.rs
+++ b/ergo-lib/src/eval/context.rs
@@ -1,4 +1,4 @@
-use crate::ast::Constant;
+use crate::ast::constant::Constant;
 use crate::chain::ergo_state_context::ErgoStateContext;
 use crate::wallet::signing::TransactionContext;
 
diff --git a/ergo-lib/src/eval/cost_accum.rs b/ergo-lib/src/eval/cost_accum.rs
index 391ccb1bd..523866c82 100644
--- a/ergo-lib/src/eval/cost_accum.rs
+++ b/ergo-lib/src/eval/cost_accum.rs
@@ -1,5 +1,5 @@
 use super::costs::{Cost, Costs};
-use crate::ast::Expr;
+use crate::ast::expr::Expr;
 
 pub struct CostAccumulator {
     costs: Costs,
diff --git a/ergo-lib/src/eval/costs.rs b/ergo-lib/src/eval/costs.rs
index d8ff7ea61..ec36279a4 100644
--- a/ergo-lib/src/eval/costs.rs
+++ b/ergo-lib/src/eval/costs.rs
@@ -1,4 +1,4 @@
-use crate::ast::Expr;
+use crate::ast::expr::Expr;
 
 pub struct Cost(u32);
 
diff --git a/ergo-lib/src/lib.rs b/ergo-lib/src/lib.rs
index a774420eb..d67602bf9 100644
--- a/ergo-lib/src/lib.rs
+++ b/ergo-lib/src/lib.rs
@@ -13,14 +13,14 @@
 #![allow(clippy::unit_arg)]
 
 mod big_integer;
-mod types;
+mod eval;
 
 pub mod ast;
 pub mod chain;
 pub mod constants;
-pub mod eval;
 pub mod serialization;
 pub mod sigma_protocol;
+pub mod types;
 pub mod util;
 pub mod wallet;
 
diff --git a/ergo-lib/src/serialization.rs b/ergo-lib/src/serialization.rs
index 01d621ba3..ed3450b94 100644
--- a/ergo-lib/src/serialization.rs
+++ b/ergo-lib/src/serialization.rs
@@ -7,12 +7,12 @@ mod expr;
 mod fold;
 mod sigmaboolean;
 
-pub mod constant_store;
-pub mod ergo_box;
-pub mod op_code;
-pub mod sigma_byte_reader;
-pub mod sigma_byte_writer;
-pub mod types;
+pub(crate) mod constant_store;
+pub(crate) mod ergo_box;
+pub(crate) mod op_code;
+pub(crate) mod sigma_byte_reader;
+pub(crate) mod sigma_byte_writer;
+pub(crate) mod types;
 
 mod serializable;
 pub use serializable::*;
diff --git a/ergo-lib/src/serialization/constant.rs b/ergo-lib/src/serialization/constant.rs
index 47279f02d..d7f02439f 100644
--- a/ergo-lib/src/serialization/constant.rs
+++ b/ergo-lib/src/serialization/constant.rs
@@ -1,8 +1,9 @@
 use super::{data::DataSerializer, sigma_byte_writer::SigmaByteWrite};
+use crate::ast::constant::Constant;
 use crate::serialization::{
     sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable,
 };
-use crate::{ast::Constant, types::SType};
+use crate::types::stype::SType;
 
 use std::io;
 
diff --git a/ergo-lib/src/serialization/constant_placeholder.rs b/ergo-lib/src/serialization/constant_placeholder.rs
index 94cfc3bd7..74ed9540a 100644
--- a/ergo-lib/src/serialization/constant_placeholder.rs
+++ b/ergo-lib/src/serialization/constant_placeholder.rs
@@ -1,5 +1,5 @@
 use super::sigma_byte_writer::SigmaByteWrite;
-use crate::ast::ConstantPlaceholder;
+use crate::ast::constant::ConstantPlaceholder;
 use crate::serialization::{
     sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable,
 };
@@ -27,12 +27,10 @@ impl SigmaSerializable for ConstantPlaceholder {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::{
-        ast::Constant,
-        serialization::{
-            constant_store::ConstantStore, sigma_byte_reader::SigmaByteReader,
-            sigma_byte_writer::SigmaByteWriter,
-        },
+    use crate::ast::constant::Constant;
+    use crate::serialization::{
+        constant_store::ConstantStore, sigma_byte_reader::SigmaByteReader,
+        sigma_byte_writer::SigmaByteWriter,
     };
     use io::Cursor;
     use proptest::prelude::*;
diff --git a/ergo-lib/src/serialization/constant_store.rs b/ergo-lib/src/serialization/constant_store.rs
index d549972f1..62a280b3d 100644
--- a/ergo-lib/src/serialization/constant_store.rs
+++ b/ergo-lib/src/serialization/constant_store.rs
@@ -1,6 +1,6 @@
 //! Constant store for Sigma byte reader
 
-use crate::ast::{Constant, ConstantPlaceholder};
+use crate::ast::constant::{Constant, ConstantPlaceholder};
 
 /// Storage for constants used in ErgoTree constant segregation
 pub struct ConstantStore {
@@ -42,7 +42,8 @@ impl ConstantStore {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::{ast::ConstantVal, types::SType};
+    use crate::ast::value::Value;
+    use crate::types::stype::SType;
 
     #[test]
     fn test_empty() {
@@ -55,7 +56,7 @@ mod tests {
     fn test_non_empty() {
         let c = Constant {
             tpe: SType::SBoolean,
-            v: ConstantVal::Boolean(true),
+            v: Value::Boolean(true),
         };
         let s = ConstantStore::new(vec![c.clone()]);
         assert!(s.get(0).is_some());
@@ -68,7 +69,7 @@ mod tests {
     fn test_put() {
         let c = Constant {
             tpe: SType::SBoolean,
-            v: ConstantVal::Boolean(true),
+            v: Value::Boolean(true),
         };
         let mut s = ConstantStore::empty();
         s.put(c.clone());
diff --git a/ergo-lib/src/serialization/data.rs b/ergo-lib/src/serialization/data.rs
index 782481d97..6ae1dec49 100644
--- a/ergo-lib/src/serialization/data.rs
+++ b/ergo-lib/src/serialization/data.rs
@@ -1,16 +1,14 @@
+use crate::ast::value::Coll;
+use crate::ast::value::CollPrim;
+use crate::ast::value::Value;
 use crate::serialization::{
     sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable,
 };
-use crate::util::AsVecU8;
-use crate::{
-    ast::ConstantVal,
-    ast::ConstantVal::*,
-    ast::{CollPrim, ConstantColl},
-    sigma_protocol,
-    types::SType,
-    types::SType::*,
+use crate::sigma_protocol::{
+    dlog_group::EcPoint, sigma_boolean::SigmaBoolean, sigma_boolean::SigmaProp,
 };
-use sigma_protocol::{dlog_group::EcPoint, sigma_boolean::SigmaBoolean, sigma_boolean::SigmaProp};
+use crate::types::stype::SType;
+use crate::util::AsVecU8;
 
 use super::sigma_byte_writer::SigmaByteWrite;
 use std::io;
@@ -18,52 +16,53 @@ use std::io;
 pub struct DataSerializer {}
 
 impl DataSerializer {
-    pub fn sigma_serialize<W: SigmaByteWrite>(c: &ConstantVal, w: &mut W) -> Result<(), io::Error> {
+    pub fn sigma_serialize<W: SigmaByteWrite>(c: &Value, w: &mut W) -> Result<(), io::Error> {
         // for reference see http://github.com/ScorexFoundation/sigmastate-interpreter/blob/25251c1313b0131835f92099f02cef8a5d932b5e/sigmastate/src/main/scala/sigmastate/serialization/DataSerializer.scala#L26-L26
         match c {
-            Boolean(v) => w.put_u8(if *v { 1 } else { 0 }),
-            Byte(v) => w.put_i8(*v),
-            Short(v) => w.put_i16(*v),
-            Int(v) => w.put_i32(*v),
-            Long(v) => w.put_i64(*v),
-            BigInt => todo!(),
-            GroupElement(ecp) => ecp.sigma_serialize(w),
-            SigmaProp(s) => s.value().sigma_serialize(w),
-            CBox(_) => todo!(),
-            AvlTree => todo!(),
-            Coll(ct) => match ct {
-                ConstantColl::Primitive(CollPrim::CollByte(b)) => {
+            Value::Boolean(v) => w.put_u8(if *v { 1 } else { 0 }),
+            Value::Byte(v) => w.put_i8(*v),
+            Value::Short(v) => w.put_i16(*v),
+            Value::Int(v) => w.put_i32(*v),
+            Value::Long(v) => w.put_i64(*v),
+            Value::BigInt => todo!(),
+            Value::GroupElement(ecp) => ecp.sigma_serialize(w),
+            Value::SigmaProp(s) => s.value().sigma_serialize(w),
+            Value::CBox(_) => todo!(),
+            Value::AvlTree => todo!(),
+            Value::Coll(ct) => match ct {
+                Coll::Primitive(CollPrim::CollByte(b)) => {
                     w.put_usize_as_u16(b.len())?;
                     w.write_all(b.clone().as_vec_u8().as_slice())
                 }
-                ConstantColl::NonPrimitive { elem_tpe: _, v } => {
+                Coll::NonPrimitive { elem_tpe: _, v } => {
                     w.put_usize_as_u16(v.len())?;
                     v.iter()
                         .try_for_each(|e| DataSerializer::sigma_serialize(e, w))
                 }
             },
-            Tup(_) => todo!(),
+            Value::Tup(_) => todo!(),
         }
     }
 
     pub fn sigma_parse<R: SigmaByteRead>(
         tpe: &SType,
         r: &mut R,
-    ) -> Result<ConstantVal, SerializationError> {
+    ) -> Result<Value, SerializationError> {
         // for reference see http://github.com/ScorexFoundation/sigmastate-interpreter/blob/25251c1313b0131835f92099f02cef8a5d932b5e/sigmastate/src/main/scala/sigmastate/serialization/DataSerializer.scala#L84-L84
+        use SType::*;
         Ok(match tpe {
-            SBoolean => Boolean(r.get_u8()? != 0),
-            SByte => Byte(r.get_i8()?),
-            SShort => Short(r.get_i16()?),
-            SInt => Int(r.get_i32()?),
-            SLong => Long(r.get_i64()?),
-            SGroupElement => GroupElement(Box::new(EcPoint::sigma_parse(r)?)),
-            SSigmaProp => ConstantVal::sigma_prop(SigmaProp::new(SigmaBoolean::sigma_parse(r)?)),
+            SBoolean => Value::Boolean(r.get_u8()? != 0),
+            SByte => Value::Byte(r.get_i8()?),
+            SShort => Value::Short(r.get_i16()?),
+            SInt => Value::Int(r.get_i32()?),
+            SLong => Value::Long(r.get_i64()?),
+            SGroupElement => Value::GroupElement(Box::new(EcPoint::sigma_parse(r)?)),
+            SSigmaProp => Value::sigma_prop(SigmaProp::new(SigmaBoolean::sigma_parse(r)?)),
             SColl(elem_type) if **elem_type == SByte => {
                 let len = r.get_u16()? as usize;
                 let mut buf = vec![0u8; len];
                 r.read_exact(&mut buf)?;
-                Coll(ConstantColl::Primitive(CollPrim::CollByte(
+                Value::Coll(Coll::Primitive(CollPrim::CollByte(
                     buf.into_iter().map(|v| v as i8).collect(),
                 )))
             }
@@ -73,7 +72,7 @@ impl DataSerializer {
                 for _ in 0..len {
                     elems.push(DataSerializer::sigma_parse(elem_type, r)?);
                 }
-                Coll(ConstantColl::NonPrimitive {
+                Value::Coll(Coll::NonPrimitive {
                     elem_tpe: *elem_type.clone(),
                     v: elems,
                 })
@@ -83,7 +82,7 @@ impl DataSerializer {
                 types.iter().try_for_each(|tpe| {
                     DataSerializer::sigma_parse(tpe, r).map(|v| items.push(v))
                 })?;
-                Tup(items)
+                Value::Tup(items)
             }
 
             c => {
diff --git a/ergo-lib/src/serialization/ergo_box.rs b/ergo-lib/src/serialization/ergo_box.rs
index b856d6a50..6532fccd5 100644
--- a/ergo-lib/src/serialization/ergo_box.rs
+++ b/ergo-lib/src/serialization/ergo_box.rs
@@ -1,6 +1,6 @@
 //! ErgoBox and ErgoBoxCandidate serialization
+use crate::ast::constant::Constant;
 use crate::{
-    ast::Constant,
     chain::{
         ergo_box::{BoxValue, ErgoBoxCandidate, NonMandatoryRegisters},
         token::{Token, TokenId},
diff --git a/ergo-lib/src/serialization/expr.rs b/ergo-lib/src/serialization/expr.rs
index 25db8d542..bb2ae1701 100644
--- a/ergo-lib/src/serialization/expr.rs
+++ b/ergo-lib/src/serialization/expr.rs
@@ -1,5 +1,8 @@
 use super::{fold::FoldSerializer, op_code::OpCode, sigma_byte_writer::SigmaByteWrite};
-use crate::ast::{CollMethods, Constant, ConstantPlaceholder, Expr};
+use crate::ast::coll_methods::CollM;
+use crate::ast::constant::Constant;
+use crate::ast::constant::ConstantPlaceholder;
+use crate::ast::expr::Expr;
 use crate::serialization::{
     sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable,
 };
@@ -22,7 +25,7 @@ impl SigmaSerializable for Expr {
                 op_code.sigma_serialize(w)?;
                 match expr {
                     Expr::CollM(cm) => match cm {
-                        CollMethods::Fold { .. } => FoldSerializer::sigma_serialize(expr, w),
+                        CollM::Fold { .. } => FoldSerializer::sigma_serialize(expr, w),
                     },
                     Expr::ConstPlaceholder(cp) => cp.sigma_serialize(w),
                     _ => panic!(format!("don't know how to serialize {:?}", expr)),
diff --git a/ergo-lib/src/serialization/fold.rs b/ergo-lib/src/serialization/fold.rs
index 6697c5481..33af5e6b9 100644
--- a/ergo-lib/src/serialization/fold.rs
+++ b/ergo-lib/src/serialization/fold.rs
@@ -1,5 +1,6 @@
 use super::{op_code::OpCode, sigma_byte_writer::SigmaByteWrite};
-use crate::ast::{CollMethods, Expr};
+use crate::ast::coll_methods::CollM;
+use crate::ast::expr::Expr;
 use crate::serialization::{
     sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable,
 };
@@ -13,7 +14,7 @@ impl FoldSerializer {
 
     pub fn sigma_serialize<W: SigmaByteWrite>(expr: &Expr, w: &mut W) -> Result<(), io::Error> {
         match expr {
-            Expr::CollM(CollMethods::Fold {
+            Expr::CollM(CollM::Fold {
                 input,
                 zero,
                 fold_op,
@@ -31,7 +32,7 @@ impl FoldSerializer {
         let input = Expr::sigma_parse(r)?;
         let zero = Expr::sigma_parse(r)?;
         let fold_op = Expr::sigma_parse(r)?;
-        Ok(Expr::CollM(CollMethods::Fold {
+        Ok(Expr::CollM(CollM::Fold {
             input: Box::new(input),
             zero: Box::new(zero),
             fold_op: Box::new(fold_op),
diff --git a/ergo-lib/src/serialization/types.rs b/ergo-lib/src/serialization/types.rs
index 423f39040..7be552f16 100644
--- a/ergo-lib/src/serialization/types.rs
+++ b/ergo-lib/src/serialization/types.rs
@@ -4,7 +4,7 @@ use super::sigma_byte_writer::SigmaByteWrite;
 use crate::serialization::{
     sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable,
 };
-use crate::types::SType;
+use crate::types::stype::SType;
 use sigma_ser::vlq_encode;
 use std::{io, ops::Add};
 use vlq_encode::WriteSigmaVlqExt;
@@ -92,6 +92,17 @@ fn is_stype_embeddable(tpe: &SType) -> bool {
     }
 }
 
+/**
+ * Each SType is serialized to array of bytes by:
+ * - emitting typeCode of each node (see special case for collections below)
+ * - then recursively serializing subtrees from left to right on each level
+ * - for each collection of primitive type there is special type code to emit single byte instead of two bytes
+ * Types code intervals
+ * - (1 .. MaxPrimTypeCode)  // primitive types
+ * - (CollectionTypeCode .. CollectionTypeCode + MaxPrimTypeCode) // collections of primitive types
+ * - (MaxCollectionTypeCode ..)  // Other types
+ * Collection of non-primitive type is serialized as (CollectionTypeCode, serialize(elementType))
+ */
 impl SigmaSerializable for SType {
     fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
         // for reference see http://github.com/ScorexFoundation/sigmastate-interpreter/blob/25251c1313b0131835f92099f02cef8a5d932b5e/sigmastate/src/main/scala/sigmastate/serialization/TypeSerializer.scala#L25-L25
diff --git a/ergo-lib/src/sigma_protocol/fiat_shamir.rs b/ergo-lib/src/sigma_protocol/fiat_shamir.rs
index 3f7db902b..54f6cb477 100644
--- a/ergo-lib/src/sigma_protocol/fiat_shamir.rs
+++ b/ergo-lib/src/sigma_protocol/fiat_shamir.rs
@@ -6,7 +6,7 @@ use super::{
     unproven_tree::UnprovenTree,
     ProofTree, ProofTreeLeaf, ProverMessage, GROUP_SIZE, SOUNDNESS_BYTES,
 };
-use crate::{ast::Expr, ergo_tree::ErgoTree, serialization::SigmaSerializable};
+use crate::{ast::expr::Expr, ergo_tree::ErgoTree, serialization::SigmaSerializable};
 use blake2::digest::{Update, VariableOutput};
 use blake2::VarBlake2b;
 use std::{
diff --git a/ergo-lib/src/sigma_protocol/prover.rs b/ergo-lib/src/sigma_protocol/prover.rs
index 57a0e1ed1..85a1d27ed 100644
--- a/ergo-lib/src/sigma_protocol/prover.rs
+++ b/ergo-lib/src/sigma_protocol/prover.rs
@@ -300,18 +300,18 @@ impl Prover for TestProver {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::{
-        ast::{Constant, ConstantVal, Expr},
-        sigma_protocol::private_input::DlogProverInput,
-        types::SType,
-    };
+    use crate::ast::constant::Constant;
+    use crate::ast::expr::Expr;
+    use crate::ast::value::Value;
+    use crate::sigma_protocol::private_input::DlogProverInput;
+    use crate::types::stype::SType;
     use std::rc::Rc;
 
     #[test]
     fn test_prove_true_prop() {
         let bool_true_tree = ErgoTree::from(Rc::new(Expr::Const(Constant {
             tpe: SType::SBoolean,
-            v: ConstantVal::Boolean(true),
+            v: Value::Boolean(true),
         })));
         let message = vec![0u8; 100];
 
@@ -330,7 +330,7 @@ mod tests {
     fn test_prove_false_prop() {
         let bool_false_tree = ErgoTree::from(Rc::new(Expr::Const(Constant {
             tpe: SType::SBoolean,
-            v: ConstantVal::Boolean(false),
+            v: Value::Boolean(false),
         })));
         let message = vec![0u8; 100];
 
diff --git a/ergo-lib/src/sigma_protocol/prover/context_extension.rs b/ergo-lib/src/sigma_protocol/prover/context_extension.rs
index 7fcf3768b..404c480c5 100644
--- a/ergo-lib/src/sigma_protocol/prover/context_extension.rs
+++ b/ergo-lib/src/sigma_protocol/prover/context_extension.rs
@@ -1,10 +1,8 @@
 //! ContextExtension type
-use crate::{
-    ast::Constant,
-    serialization::{
-        sigma_byte_reader::SigmaByteRead, sigma_byte_writer::SigmaByteWrite, SerializationError,
-        SigmaSerializable,
-    },
+use crate::ast::constant::Constant;
+use crate::serialization::{
+    sigma_byte_reader::SigmaByteRead, sigma_byte_writer::SigmaByteWrite, SerializationError,
+    SigmaSerializable,
 };
 use indexmap::IndexMap;
 #[cfg(feature = "json")]
diff --git a/ergo-lib/src/sigma_protocol/verifier.rs b/ergo-lib/src/sigma_protocol/verifier.rs
index 7da2186af..1171e0118 100644
--- a/ergo-lib/src/sigma_protocol/verifier.rs
+++ b/ergo-lib/src/sigma_protocol/verifier.rs
@@ -119,14 +119,13 @@ impl Verifier for TestVerifier {}
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::{
-        ast::{Constant, Expr},
-        sigma_protocol::{
-            private_input::{DlogProverInput, PrivateInput},
-            prover::{Prover, TestProver},
-        },
-        types::SType,
+    use crate::ast::constant::Constant;
+    use crate::ast::expr::Expr;
+    use crate::sigma_protocol::{
+        private_input::{DlogProverInput, PrivateInput},
+        prover::{Prover, TestProver},
     };
+    use crate::types::stype::SType;
     use proptest::prelude::*;
     use std::rc::Rc;
 
diff --git a/ergo-lib/src/types.rs b/ergo-lib/src/types.rs
index 5b72ed877..c7cfd173e 100644
--- a/ergo-lib/src/types.rs
+++ b/ergo-lib/src/types.rs
@@ -1,166 +1,8 @@
 //! Sigma types
 
-use super::serialization::types::TypeCode;
+pub mod stype;
 
-#[derive(PartialEq, Eq, Debug, Clone)]
-pub struct MethodId(u8);
-#[derive(PartialEq, Eq, Debug, Clone)]
-pub struct TypeId(u8);
-
-#[derive(PartialEq, Eq, Debug, Clone)]
-pub enum SType {
-    SAny,
-    SBoolean,
-    SByte,
-    SShort,
-    SInt,
-    SLong,
-    SBigInt,
-    SGroupElement,
-    SSigmaProp,
-    SBox,
-    SAvlTree,
-    SOption(Box<SType>),
-    SColl(Box<SType>),
-    STup(Vec<SType>),
-    SFunc(Box<SFunc>),
-}
-
-impl SType {
-    pub fn type_code(&self) -> TypeCode {
-        match self {
-            SType::SAny => todo!(),
-            SType::SBoolean => TypeCode::SBOOLEAN,
-            SType::SByte => TypeCode::SBYTE,
-            SType::SShort => TypeCode::SSHORT,
-            SType::SInt => TypeCode::SINT,
-            SType::SLong => TypeCode::SLONG,
-            SType::SBigInt => TypeCode::SBIGINT,
-            SType::SGroupElement => TypeCode::SGROUP_ELEMENT,
-            SType::SSigmaProp => TypeCode::SSIGMAPROP,
-            SType::SBox => todo!(),
-            SType::SAvlTree => todo!(),
-            SType::SOption(_) => todo!(),
-            SType::SColl(_) => todo!(),
-            SType::STup(_) => todo!(),
-            SType::SFunc(_) => todo!(),
-        }
-    }
-
-    pub fn type_companion(&self) -> Option<STypeCompanion> {
-        todo!()
-    }
-
-    pub fn new_scoll(elem_type: SType) -> SType {
-        SType::SColl(Box::new(elem_type))
-    }
-}
-
-/// Conversion to SType
-pub trait LiftIntoSType {
-    /// get SType
-    fn stype() -> SType;
-}
-
-impl<T: LiftIntoSType> LiftIntoSType for Vec<T> {
-    fn stype() -> SType {
-        SType::SColl(Box::new(T::stype()))
-    }
-}
-
-impl LiftIntoSType for bool {
-    fn stype() -> SType {
-        SType::SBoolean
-    }
-}
-
-impl LiftIntoSType for i8 {
-    fn stype() -> SType {
-        SType::SByte
-    }
-}
-
-impl LiftIntoSType for i16 {
-    fn stype() -> SType {
-        SType::SShort
-    }
-}
-
-impl LiftIntoSType for i32 {
-    fn stype() -> SType {
-        SType::SInt
-    }
-}
-
-impl LiftIntoSType for i64 {
-    fn stype() -> SType {
-        SType::SLong
-    }
-}
-
-#[derive(PartialEq, Eq, Debug, Clone)]
-pub struct STypeVar {
-    name: String,
-}
-
-#[derive(PartialEq, Eq, Debug, Clone)]
-pub struct STypeParam {
-    ident: STypeVar,
-    upper_bound: Option<SType>,
-    lower_bound: Option<SType>,
-}
-
-#[derive(PartialEq, Eq, Debug, Clone)]
-pub struct SFunc {
-    t_dom: Vec<SType>,
-    t_range: SType,
-    tpe_params: Vec<STypeParam>,
-}
-
-#[derive(PartialEq, Eq, Debug, Clone)]
-pub struct STypeCompanion {
-    pub type_id: TypeId,
-    pub type_name: String,
-    pub methods: Vec<SMethod>,
-}
-
-#[derive(PartialEq, Eq, Debug, Clone)]
-pub struct SMethod {
-    pub obj_type: Box<STypeCompanion>,
-    pub name: String,
-    pub method_id: MethodId,
-    pub tpe: SType,
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use proptest::prelude::*;
-
-    fn primitive_type() -> BoxedStrategy<SType> {
-        prop_oneof![
-            Just(SType::SBoolean),
-            Just(SType::SByte),
-            Just(SType::SShort),
-            Just(SType::SInt),
-            Just(SType::SLong),
-            Just(SType::SBigInt),
-            Just(SType::SGroupElement),
-            Just(SType::SSigmaProp),
-        ]
-        .boxed()
-    }
-
-    impl Arbitrary for SType {
-        type Parameters = ();
-        type Strategy = BoxedStrategy<Self>;
-
-        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
-            prop_oneof![
-                primitive_type(),
-                primitive_type().prop_map(SType::new_scoll),
-            ]
-            .boxed()
-        }
-    }
-}
+pub(crate) mod sfunc;
+pub(crate) mod smethod;
+pub(crate) mod stype_companion;
+pub(crate) mod stype_param;
diff --git a/ergo-lib/src/types/sfunc.rs b/ergo-lib/src/types/sfunc.rs
new file mode 100644
index 000000000..cd1c7095c
--- /dev/null
+++ b/ergo-lib/src/types/sfunc.rs
@@ -0,0 +1,9 @@
+use super::stype::SType;
+use super::stype_param::STypeParam;
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct SFunc {
+    t_dom: Vec<SType>,
+    t_range: SType,
+    tpe_params: Vec<STypeParam>,
+}
diff --git a/ergo-lib/src/types/smethod.rs b/ergo-lib/src/types/smethod.rs
new file mode 100644
index 000000000..f3314a2d0
--- /dev/null
+++ b/ergo-lib/src/types/smethod.rs
@@ -0,0 +1,14 @@
+use super::stype::SType;
+use super::stype_companion::STypeCompanion;
+
+/// Method id unique among the methods of the same object
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct MethodId(u8);
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct SMethod {
+    pub obj_type: Box<STypeCompanion>,
+    pub name: String,
+    pub method_id: MethodId,
+    pub tpe: SType,
+}
diff --git a/ergo-lib/src/types/stype.rs b/ergo-lib/src/types/stype.rs
new file mode 100644
index 000000000..7ec601c18
--- /dev/null
+++ b/ergo-lib/src/types/stype.rs
@@ -0,0 +1,149 @@
+//! SType hierarchy
+
+use crate::serialization::types::TypeCode;
+
+use super::sfunc::SFunc;
+use super::stype_companion::STypeCompanion;
+
+/// Every type descriptor is a tree represented by nodes in SType hierarchy.
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub enum SType {
+    /// TBD
+    SAny,
+    /// Boolean
+    SBoolean,
+    /// Signed byte
+    SByte,
+    /// Signed short (16-bit)
+    SShort,
+    /// Signed int (32-bit)
+    SInt,
+    /// Signed long (64-bit)
+    SLong,
+    /// 256-bit integer
+    SBigInt,
+    /// Discrete logarithm prime-order group element [`EcPoint`]
+    SGroupElement,
+    /// Proposition which can be proven and verified by sigma protocol.
+    SSigmaProp,
+    /// ErgoBox value
+    SBox,
+    /// AVL tree value
+    SAvlTree,
+    /// Optional value
+    SOption(Box<SType>),
+    /// Collection of elements of the same type
+    SColl(Box<SType>),
+    /// Tuple (elements can have different types)
+    STup(Vec<SType>),
+    /// Function (signature)
+    SFunc(Box<SFunc>),
+}
+
+impl SType {
+    /// Type code used in serialization of SType values.
+    pub fn type_code(&self) -> TypeCode {
+        match self {
+            SType::SAny => todo!(),
+            SType::SBoolean => TypeCode::SBOOLEAN,
+            SType::SByte => TypeCode::SBYTE,
+            SType::SShort => TypeCode::SSHORT,
+            SType::SInt => TypeCode::SINT,
+            SType::SLong => TypeCode::SLONG,
+            SType::SBigInt => TypeCode::SBIGINT,
+            SType::SGroupElement => TypeCode::SGROUP_ELEMENT,
+            SType::SSigmaProp => TypeCode::SSIGMAPROP,
+            SType::SBox => todo!(),
+            SType::SAvlTree => todo!(),
+            SType::SOption(_) => todo!(),
+            SType::SColl(_) => todo!(),
+            SType::STup(_) => todo!(),
+            SType::SFunc(_) => todo!(),
+        }
+    }
+
+    /// Get STypeCompanion instance associated with this SType
+    pub fn type_companion(&self) -> Option<STypeCompanion> {
+        todo!()
+    }
+
+    /// Create new SColl with the given element type
+    pub fn new_scoll(elem_type: SType) -> SType {
+        SType::SColl(Box::new(elem_type))
+    }
+}
+
+/// Conversion to SType
+pub trait LiftIntoSType {
+    /// get SType
+    fn stype() -> SType;
+}
+
+impl<T: LiftIntoSType> LiftIntoSType for Vec<T> {
+    fn stype() -> SType {
+        SType::SColl(Box::new(T::stype()))
+    }
+}
+
+impl LiftIntoSType for bool {
+    fn stype() -> SType {
+        SType::SBoolean
+    }
+}
+
+impl LiftIntoSType for i8 {
+    fn stype() -> SType {
+        SType::SByte
+    }
+}
+
+impl LiftIntoSType for i16 {
+    fn stype() -> SType {
+        SType::SShort
+    }
+}
+
+impl LiftIntoSType for i32 {
+    fn stype() -> SType {
+        SType::SInt
+    }
+}
+
+impl LiftIntoSType for i64 {
+    fn stype() -> SType {
+        SType::SLong
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use proptest::prelude::*;
+
+    fn primitive_type() -> BoxedStrategy<SType> {
+        prop_oneof![
+            Just(SType::SBoolean),
+            Just(SType::SByte),
+            Just(SType::SShort),
+            Just(SType::SInt),
+            Just(SType::SLong),
+            Just(SType::SBigInt),
+            Just(SType::SGroupElement),
+            Just(SType::SSigmaProp),
+        ]
+        .boxed()
+    }
+
+    impl Arbitrary for SType {
+        type Parameters = ();
+        type Strategy = BoxedStrategy<Self>;
+
+        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
+            prop_oneof![
+                primitive_type(),
+                primitive_type().prop_map(SType::new_scoll),
+            ]
+            .boxed()
+        }
+    }
+}
diff --git a/ergo-lib/src/types/stype_companion.rs b/ergo-lib/src/types/stype_companion.rs
new file mode 100644
index 000000000..99abd0fd1
--- /dev/null
+++ b/ergo-lib/src/types/stype_companion.rs
@@ -0,0 +1,11 @@
+use super::smethod::SMethod;
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct TypeId(u8);
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct STypeCompanion {
+    pub type_id: TypeId,
+    pub type_name: String,
+    pub methods: Vec<SMethod>,
+}
diff --git a/ergo-lib/src/types/stype_param.rs b/ergo-lib/src/types/stype_param.rs
new file mode 100644
index 000000000..a32477cef
--- /dev/null
+++ b/ergo-lib/src/types/stype_param.rs
@@ -0,0 +1,13 @@
+use super::stype::SType;
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct STypeVar {
+    name: String,
+}
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct STypeParam {
+    ident: STypeVar,
+    upper_bound: Option<SType>,
+    lower_bound: Option<SType>,
+}
diff --git a/ergo-lib/src/util.rs b/ergo-lib/src/util.rs
index d2bbf274b..7044b92da 100644
--- a/ergo-lib/src/util.rs
+++ b/ergo-lib/src/util.rs
@@ -3,7 +3,7 @@
 use elliptic_curve::subtle::CtOption;
 
 /// Convert to Option<T>
-pub trait IntoOption<T> {
+pub(crate) trait IntoOption<T> {
     /// Get Option<T>
     fn into_option(self) -> Option<T>;
 }
diff --git a/ergo-lib/src/wallet/signing.rs b/ergo-lib/src/wallet/signing.rs
index 5a1812f26..f0a548410 100644
--- a/ergo-lib/src/wallet/signing.rs
+++ b/ergo-lib/src/wallet/signing.rs
@@ -84,8 +84,10 @@ mod tests {
     use proptest::collection::vec;
     use proptest::prelude::*;
 
+    use crate::ast::constant::Constant;
+    use crate::ast::expr::Expr;
+    use crate::types::stype::SType;
     use crate::{
-        ast::{Constant, Expr},
         chain::{
             ergo_box::{box_builder::ErgoBoxCandidateBuilder, BoxValue, NonMandatoryRegisters},
             transaction::{TxId, UnsignedInput},
@@ -96,7 +98,6 @@ mod tests {
             prover::TestProver,
             verifier::{TestVerifier, Verifier, VerifierError},
         },
-        types::SType,
     };
     use std::rc::Rc;
 
diff --git a/ergo-lib/src/wallet/tx_builder.rs b/ergo-lib/src/wallet/tx_builder.rs
index 6b2cc9337..33363a59a 100644
--- a/ergo-lib/src/wallet/tx_builder.rs
+++ b/ergo-lib/src/wallet/tx_builder.rs
@@ -185,7 +185,7 @@ impl<S: ErgoBoxAssets + ErgoBoxId + Clone> TxBuilder<S> {
         // add miner's fee
         let miner_fee_box = new_miner_fee_box(self.fee_amount, self.current_height)?;
         output_candidates.push(miner_fee_box);
-        if output_candidates.len() > u16::MAX as usize {
+        if output_candidates.len() > Transaction::MAX_OUTPUTS_COUNT {
             return Err(TxBuilderError::InvalidArgs("too many outputs".to_string()));
         }
         // check that inputs have enough coins


Test patch: