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

Issue:
Add Context::self, outputs, dataInputs
- assemble and pass values;
- add IR nodes (eval, serialization, costing); 


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 000000000..d4977da8d
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,122 @@
+name: Tests
+
+on:
+  push:
+    branches:
+      - master
+      - develop
+  pull_request:
+    types:
+      - opened
+      - synchronize
+
+jobs:
+  rustfmt:
+    name: rustfmt
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions-rs/toolchain@v1
+        with:
+          toolchain: stable
+          components: rustfmt
+          override: true
+      # Ensure all code has been formatted with rustfmt
+      - name: Check formatting
+        uses: actions-rs/cargo@v1
+        with:
+          command: fmt
+          args: --all -- --check --color always
+
+  clippy:
+    name: clippy
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions-rs/toolchain@v1
+        with:
+          toolchain: stable
+          components: clippy
+          override: true
+      # Ensure all code has been formatted with rustfmt
+      - name: Check with Clippy
+        uses: actions-rs/clippy-check@v1
+        with:
+          token: ${{ secrets.GITHUB_TOKEN }}
+          args: --all-features -- -D warnings
+
+  build_wo_default_features:
+    name: Build without default features
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions-rs/toolchain@v1
+        with:
+          toolchain: stable
+          override: true
+      - name: Build
+        uses: actions-rs/cargo@v1
+        with:
+          command: build
+          args: --no-default-features --manifest-path ergo-lib/Cargo.toml
+
+  test:
+    name: Test on ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ubuntu-latest, windows-latest, macOS-latest]
+
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions-rs/toolchain@v1
+        with:
+          toolchain: stable
+          override: true
+      - name: cargo fetch
+        uses: actions-rs/cargo@v1
+        with:
+          command: fetch
+      - name: Build tests
+        uses: actions-rs/cargo@v1
+        with:
+          command: build
+          args: --verbose --release --tests
+      - name: Run tests
+        uses: actions-rs/cargo@v1
+        with:
+          command: test
+          args: --verbose --release
+      - name: rust-tarpaulin code coverage check
+        if: matrix.os == 'ubuntu-latest' && github.event_name == 'pull_request'
+        uses: actions-rs/tarpaulin@master
+        with:
+          args: '-v --out Lcov --exclude-files bindings/*'
+      - name: Push code coverage results to coveralls.io
+        if: matrix.os == 'ubuntu-latest' && github.event_name == 'pull_request'
+        uses: coverallsapp/github-action@master
+        with:
+          github-token: ${{ secrets.GITHUB_TOKEN }}
+          path-to-lcov: ./lcov.info
+        
+  doc-links:
+    name: Intra-documentation links
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions-rs/toolchain@v1
+        with:
+          toolchain: stable
+          override: true
+      - name: cargo fetch
+        uses: actions-rs/cargo@v1
+        with:
+          command: fetch
+      # Ensure intra-documentation links all resolve correctly
+      # Requires #![deny(broken_intra_doc_links)] in crate.
+      - name: Check intra-doc links
+        uses: actions-rs/cargo@v1
+        with:
+          command: doc
+          args: --document-private-items
diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml
new file mode 100644
index 000000000..6ba056fd6
--- /dev/null
+++ b/.github/workflows/ios.yml
@@ -0,0 +1,36 @@
+name: iOS build
+on:
+  push:
+    branches:
+      - master
+      - develop
+  pull_request:
+    types:
+      - opened
+      - synchronize
+
+
+jobs:
+  build:
+    name: build (iOS)
+    runs-on: macos-latest
+    steps:
+      - name: checkout
+        uses: actions/checkout@v2
+
+      - name: install deps
+        run: |
+          rustup target add aarch64-apple-ios x86_64-apple-ios
+          cargo install cargo-lipo
+          cargo install cbindgen
+
+      - name: build
+        run: |
+          cd bindings/ergo-lib-c
+          cbindgen --config cbindgen.toml --crate ergo-lib-c --output h/ergo_wallet.h
+          cd ../ergo-lib-ios
+          cargo lipo --release --targets=aarch64-apple-ios,x86_64-apple-ios
+          swift package generate-xcodeproj
+          xcodebuild -project ./ErgoLib.xcodeproj -xcconfig ./Config/Release.xcconfig -sdk iphoneos
+          xcodebuild -project ./ErgoLib.xcodeproj -xcconfig ./Config/Release.xcconfig -sdk iphonesimulator
+
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 1fb3920c9..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,109 +0,0 @@
-dist: bionic
-language: rust
-sudo: false
-
-rust:
-  - stable
-  - beta
-  - nightly
-
-os:
-  - linux
-  # - osx # it hangs
-  - windows
-
-env:
-  global:
-    - RUST_BACKTRACE=1
-
-matrix:
-  fast_finish: true
-  allow_failures:
-    - rust: nightly
-    - rust: beta
-
-  include:
-    - name: formatting
-      rust: stable
-      before_install:
-        - rustup component add rustfmt
-      script:
-        - cargo fmt --all -- --check
-
-    - name: clippy
-      rust: stable
-      before_install:
-        - rustup component add clippy
-      script:
-        - cargo clippy --all-features -- -D warnings
-
-    - name: coverage
-      rust: stable
-      sudo: required
-      dist: bionic
-      addons:
-        apt:
-          packages:
-            - libssl-dev
-      before_script:
-        - cargo install cargo-tarpaulin
-      script:
-        - cargo clean
-        - cargo build
-      after_success:
-        - cargo tarpaulin --ciserver travis-ci --coveralls $TRAVIS_JOB_ID --exclude-files bindings/*
-
-    - name: JS tests
-      dist: bionic
-      sudo: true
-      rust: stable
-      before_cache:
-        - rm -rf /home/travis/.cargo/bin/wasm-pack
-      before_install:
-        - sudo apt-get update
-        - sudo apt-get install curl
-        - curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
-        - sudo apt-get -y install nodejs
-      before_script:
-        - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f
-      script:
-        - cd bindings/ergo-lib-wasm
-        - wasm-pack build
-        - npm install && npm run test
-
-    - name: build for iOS
-      os: osx
-      osx_image: xcode12
-      rust: stable
-      before_install:
-        - rustup target add aarch64-apple-ios x86_64-apple-ios
-        - cargo install cargo-lipo
-        - cargo install cbindgen
-      script:
-        - cd bindings/ergo-lib-c
-        - cbindgen --config cbindgen.toml --crate ergo-lib-c --output h/ergo_wallet.h
-        - cd ../ergo-lib-ios
-        - cargo lipo --release --targets=aarch64-apple-ios,x86_64-apple-ios
-        - swift package generate-xcodeproj
-        - xcodebuild -project ./ErgoLib.xcodeproj -xcconfig ./Config/Release.xcconfig -sdk iphoneos
-        - xcodebuild -project ./ErgoLib.xcodeproj -xcconfig ./Config/Release.xcconfig -sdk iphonesimulator
-
-    - name: test ergo-lib without default features
-      rust: stable
-      script:
-        - cd ergo-lib
-        - cargo build --no-default-features
-
-
-
-cache: cargo
-# But don't cache the cargo registry
-before_cache:
-  - rm -rf /home/travis/.cargo/git
-  - rm -rf /home/travis/.cargo/registry
-
-script:
-  - cargo build --verbose --all
-  - cargo test --verbose --all
-  - cargo test --release --verbose --all
-  - cargo doc --no-deps
diff --git a/bindings/ergo-lib-wasm/package.json b/bindings/ergo-lib-wasm/package.json
index 77903850f..f5d0a2772 100644
--- a/bindings/ergo-lib-wasm/package.json
+++ b/bindings/ergo-lib-wasm/package.json
@@ -4,7 +4,7 @@
         "serve": "wasm-pack build --target browser && webpack-dev-server",
         "test": "wasm-pack build --target nodejs && mocha --require @babel/register --require @babel/polyfill 'tests/test*.js'",
         "doc": "jsdoc -c jsdoc.json pkg/ergo_wallet_wasm.js README.md -d docs",
-        "build-nodejs": "rm -rf ./pkg && wasm-pack build --target nodejs", 
+        "build-nodejs": "rm -rf ./pkg && wasm-pack build --target nodejs",
         "build-browser": "rm -rf ./pkg && wasm-pack build --target browser --release",
         "publish-nodejs": "npm run build-nodejs && node ./scripts/publish_helper -nodejs && cd pkg && npm publish",
         "publish-browser": "npm run build-browser && node ./scripts/publish_helper -browser && cd pkg && npm publish"
@@ -15,15 +15,15 @@
         "@babel/polyfill": "^7.6.0",
         "@babel/preset-env": "^7.6.3",
         "@babel/register": "^7.6.2",
+        "chai": "^4.2.0",
         "html-webpack-plugin": "^3.2.0",
         "jsdoc": "~3.6.3",
-        "mocha": "^6.1.4",
+        "mocha": "^6.2.3",
         "mocha-loader": "^2.0.1",
         "text-encoder": "0.0.4",
         "webpack": "^4.11.1",
         "webpack-cli": "^3.1.1",
-        "webpack-dev-server": "^3.1.0",
-        "chai": "^4.2.0"
+        "webpack-dev-server": "^3.1.0"
     },
     "dependencies": {}
 }
diff --git a/bindings/ergo-lib-wasm/src/tx_builder.rs b/bindings/ergo-lib-wasm/src/tx_builder.rs
index eff6818ee..b44db71cf 100644
--- a/bindings/ergo-lib-wasm/src/tx_builder.rs
+++ b/bindings/ergo-lib-wasm/src/tx_builder.rs
@@ -22,7 +22,7 @@ impl TxBuilder {
     }
 
     /// Creates new TxBuilder
-    /// `box_selection` - selected input boxes (via [`BoxSelector`])
+    /// `box_selection` - selected input boxes (via [`super::box_selector`])
     /// `output_candidates` - output boxes to be "created" in this transaction,
     /// `current_height` - chain height that will be used in additionally created boxes (change, miner's fee, etc.),
     /// `fee_amount` - miner's fee,
diff --git a/ergo-lib/CHANGELOG.md b/ergo-lib/CHANGELOG.md
index aa4201683..003ca292a 100644
--- a/ergo-lib/CHANGELOG.md
+++ b/ergo-lib/CHANGELOG.md
@@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 
 <!-- next-header -->
 ## [Unreleased] - ReleaseDate
+
+### Added 
+
+- Interpreter: added global vars (`INPUTS`, `OUTPUTS`, `SELF`, `HEIGHT`), `Context` properties (`dataInputs`) [#155](https://github.com/ergoplatform/sigma-rust/pull/155);
+
 ## [0.4.1] - 2020-11-19
 
 ### Added 
diff --git a/ergo-lib/Cargo.toml b/ergo-lib/Cargo.toml
index 73ec2e63a..d63ba88a9 100644
--- a/ergo-lib/Cargo.toml
+++ b/ergo-lib/Cargo.toml
@@ -26,6 +26,8 @@ thiserror = "1"
 bs58 = "0.3.1"
 num-bigint = "0.3.0"
 rand = "0.7"
+lazy_static = "1.4"
+derive_more = "0.99"
 
 [features]
 default = ["json"]
diff --git a/ergo-lib/README.md b/ergo-lib/README.md
index f5f30f5cf..ff45bf3bb 100644
--- a/ergo-lib/README.md
+++ b/ergo-lib/README.md
@@ -1,19 +1,26 @@
 [![Latest Version](https://img.shields.io/crates/v/ergo-lib.svg)](https://crates.io/crates/ergo-lib)
 [![Documentation](https://docs.rs/ergo-lib/badge.svg)](https://docs.rs/crate/ergo-lib)
 
-Features:
+## Features:
 - ErgoTree AST;
-- Interpreter;
 - Transactions, boxes, etc.;
 - JSON serialization;
-- Transaction creation and signing;
+- Box builder(with mint token support);
+- Transaction creation(builder) and signing;
+- Box selection for funds and assets (with token burning support);
 
-# Usage
+
+## Interpreter (what's implemented):
+- global vars (`INPUTS`, `OUTPUTS`, `SELF`, `HEIGHT`);
+- `Context` properties (`dataInputs`);
+
+## Usage 
 ## Crate features
-### `json`
+## `json` (default feature)
 JSON serialization for chain types using `serde`.
 
 
 
 
 
+
diff --git a/ergo-lib/src/ast.rs b/ergo-lib/src/ast.rs
index 7f9cfc8d0..1de30a74d 100644
--- a/ergo-lib/src/ast.rs
+++ b/ergo-lib/src/ast.rs
@@ -2,10 +2,12 @@
 
 pub(crate) mod box_methods;
 pub(crate) mod coll_methods;
-pub(crate) mod context_methods;
 pub(crate) mod expr;
+pub(crate) mod global_vars;
+pub(crate) mod method_call;
 pub(crate) mod ops;
 pub(crate) mod predef_func;
-pub(crate) mod value;
+pub(crate) mod property_call;
 
 pub mod constant;
+pub mod value;
diff --git a/ergo-lib/src/ast/constant.rs b/ergo-lib/src/ast/constant.rs
index 136e4bbe7..a02616e66 100644
--- a/ergo-lib/src/ast/constant.rs
+++ b/ergo-lib/src/ast/constant.rs
@@ -1,5 +1,6 @@
 //! Constant(Literal) IR node
 
+use crate::chain::ergo_box::ErgoBox;
 use crate::chain::{Base16DecodedBytes, Base16EncodedBytes};
 use crate::types::stype::LiftIntoSType;
 use crate::types::stype::SType;
@@ -20,6 +21,8 @@ use super::value::CollPrim;
 use super::value::StoredNonPrimitive;
 use super::value::Value;
 
+use thiserror::Error;
+
 #[derive(PartialEq, Eq, Debug, Clone)]
 #[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
 #[cfg_attr(
@@ -118,6 +121,15 @@ impl From<EcPoint> for Constant {
     }
 }
 
+impl From<ErgoBox> for Constant {
+    fn from(b: ErgoBox) -> Self {
+        Constant {
+            tpe: SType::SBox,
+            v: b.into(),
+        }
+    }
+}
+
 impl From<Vec<u8>> for Constant {
     fn from(v: Vec<u8>) -> Self {
         Constant {
@@ -147,14 +159,28 @@ impl<T: LiftIntoSType + StoredNonPrimitive + Into<Value>> From<Vec<T>> for Const
     }
 }
 
+/// Extract value wrapped in a type
+pub trait TryExtractInto<F> {
+    /// Extract value of the given type from any type (e.g. ['Constant'], [`super::value::Value`])
+    /// on which [`TryExtractFrom`] is implemented
+    fn try_extract_into<T: TryExtractFrom<F>>(self) -> Result<T, TryExtractFromError>;
+}
+
+impl<F> TryExtractInto<F> for F {
+    fn try_extract_into<T: TryExtractFrom<F>>(self) -> Result<T, TryExtractFromError> {
+        T::try_extract_from(self)
+    }
+}
+
 /// Underlying type is different from requested value type
-#[derive(PartialEq, Eq, Debug, Clone)]
+#[derive(Error, PartialEq, Eq, Debug, Clone)]
+#[error("Failed TryExtractFrom: {0}")]
 pub struct TryExtractFromError(pub String);
 
 /// Extract underlying value if type matches
 pub trait TryExtractFrom<T>: Sized {
     /// Extract the value or return an error if type does not match
-    fn try_extract_from(c: T) -> Result<Self, TryExtractFromError>;
+    fn try_extract_from(v: T) -> Result<Self, TryExtractFromError>;
 }
 
 impl<T: TryExtractFrom<Value>> TryExtractFrom<Constant> for T {
@@ -163,23 +189,6 @@ impl<T: TryExtractFrom<Value>> TryExtractFrom<Constant> for T {
     }
 }
 
-impl<T: TryExtractFrom<Value> + StoredNonPrimitive + LiftIntoSType> TryExtractFrom<Constant>
-    for Vec<T>
-{
-    fn try_extract_from(c: Constant) -> Result<Self, TryExtractFromError> {
-        match c.v {
-            Value::Coll(Coll::NonPrimitive { elem_tpe: _, v }) => {
-                v.into_iter().map(T::try_extract_from).collect()
-            }
-            _ => Err(TryExtractFromError(format!(
-                "expected {:?}, found {:?}",
-                std::any::type_name::<Self>(),
-                c.v
-            ))),
-        }
-    }
-}
-
 impl TryExtractFrom<Constant> for Vec<i8> {
     fn try_extract_from(c: Constant) -> Result<Self, TryExtractFromError> {
         match c.v {
diff --git a/ergo-lib/src/ast/context_methods.rs b/ergo-lib/src/ast/context_methods.rs
deleted file mode 100644
index cdf9bfd81..000000000
--- a/ergo-lib/src/ast/context_methods.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-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
index 93bf639fe..a73912c36 100644
--- a/ergo-lib/src/ast/expr.rs
+++ b/ergo-lib/src/ast/expr.rs
@@ -1,57 +1,42 @@
 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::global_vars::GlobalVars;
+use super::method_call::MethodCall;
 use super::ops;
 use super::predef_func::PredefFunc;
+use super::property_call::PropertyCall;
 
-#[derive(PartialEq, Eq, Debug, Clone)]
+extern crate derive_more;
+use derive_more::From;
+
+#[derive(PartialEq, Eq, Debug, Clone, From)]
 /// 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),
+    Context,
+    // Global(Global),
+    /// Predefined global variables
+    GlobalVars(GlobalVars),
     /// 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>,
-    },
+    MethodCall(MethodCall),
+    /// Property call
+    ProperyCall(PropertyCall),
     /// Binary operation
     BinOp(ops::BinOp, Box<Expr>, Box<Expr>),
 }
@@ -62,7 +47,11 @@ impl Expr {
         match self {
             Expr::Const(_) => todo!(),
             Expr::ConstPlaceholder(cp) => cp.op_code(),
-            _ => todo!(),
+            Expr::GlobalVars(v) => v.op_code(),
+            Expr::MethodCall(v) => v.op_code(),
+            Expr::ProperyCall(v) => v.op_code(),
+            Expr::Context => OpCode::CONTEXT,
+            _ => todo!("{0:?}", self),
         }
     }
 
@@ -81,12 +70,6 @@ impl fmt::Display for Expr {
     }
 }
 
-impl From<Constant> for Expr {
-    fn from(c: Constant) -> Self {
-        Self::Const(c)
-    }
-}
-
 #[cfg(test)]
 mod tests {
     #![allow(unused_imports)]
diff --git a/ergo-lib/src/ast/global_vars.rs b/ergo-lib/src/ast/global_vars.rs
new file mode 100644
index 000000000..3d9cf5295
--- /dev/null
+++ b/ergo-lib/src/ast/global_vars.rs
@@ -0,0 +1,41 @@
+use crate::serialization::op_code::OpCode;
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+/// Predefined global variables
+pub enum GlobalVars {
+    /// Tx inputs
+    Inputs,
+    /// Tx outputs
+    Outputs,
+    /// Current blockchain height
+    Height,
+    /// ErgoBox instance, which script is being evaluated
+    SelfBox,
+}
+
+impl GlobalVars {
+    pub fn op_code(&self) -> OpCode {
+        match self {
+            GlobalVars::SelfBox => OpCode::SELF_BOX,
+            GlobalVars::Inputs => OpCode::INPUTS,
+            GlobalVars::Outputs => OpCode::OUTPUTS,
+            GlobalVars::Height => OpCode::HEIGHT,
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use proptest::prelude::*;
+
+    impl Arbitrary for GlobalVars {
+        type Parameters = ();
+        type Strategy = BoxedStrategy<Self>;
+
+        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
+            use GlobalVars::*;
+            prop_oneof![Just(Inputs), Just(Outputs), Just(Height), Just(SelfBox),].boxed()
+        }
+    }
+}
diff --git a/ergo-lib/src/ast/method_call.rs b/ergo-lib/src/ast/method_call.rs
new file mode 100644
index 000000000..c31c868de
--- /dev/null
+++ b/ergo-lib/src/ast/method_call.rs
@@ -0,0 +1,22 @@
+use crate::serialization::op_code::OpCode;
+use crate::types::smethod::SMethod;
+use crate::types::stype::SType;
+
+use super::expr::Expr;
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct MethodCall {
+    pub obj: Box<Expr>,
+    pub method: SMethod,
+    pub args: Vec<Expr>,
+}
+
+impl MethodCall {
+    pub fn tpe(&self) -> &SType {
+        self.method.tpe()
+    }
+
+    pub fn op_code(&self) -> OpCode {
+        OpCode::METHOD_CALL
+    }
+}
diff --git a/ergo-lib/src/ast/property_call.rs b/ergo-lib/src/ast/property_call.rs
new file mode 100644
index 000000000..9616631d7
--- /dev/null
+++ b/ergo-lib/src/ast/property_call.rs
@@ -0,0 +1,21 @@
+use crate::serialization::op_code::OpCode;
+use crate::types::smethod::SMethod;
+use crate::types::stype::SType;
+
+use super::expr::Expr;
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct PropertyCall {
+    pub obj: Box<Expr>,
+    pub method: SMethod,
+}
+
+impl PropertyCall {
+    pub fn tpe(&self) -> &SType {
+        self.method.tpe()
+    }
+
+    pub fn op_code(&self) -> OpCode {
+        OpCode::PROPERTY_CALL
+    }
+}
diff --git a/ergo-lib/src/ast/value.rs b/ergo-lib/src/ast/value.rs
index 1538c083a..72f429085 100644
--- a/ergo-lib/src/ast/value.rs
+++ b/ergo-lib/src/ast/value.rs
@@ -1,6 +1,11 @@
+//! Ergo data type
+
 use std::convert::TryFrom;
+use std::rc::Rc;
 
 use crate::chain::ergo_box::ErgoBox;
+// use crate::eval::context::Context;
+use crate::eval::context::Context;
 use crate::sigma_protocol::dlog_group::EcPoint;
 use crate::sigma_protocol::sigma_boolean::ProveDlog;
 use crate::sigma_protocol::sigma_boolean::SigmaBoolean;
@@ -79,6 +84,8 @@ pub enum Value {
     Coll(Coll),
     /// Tuple (arbitrary type values)
     Tup(Vec<Value>),
+    /// Transaction(and blockchain) context info
+    Context(Rc<Context>),
 }
 
 impl Value {
@@ -130,6 +137,12 @@ impl Into<Value> for EcPoint {
     }
 }
 
+impl From<ErgoBox> for Value {
+    fn from(b: ErgoBox) -> Self {
+        Value::CBox(Box::new(b))
+    }
+}
+
 /// Marker trait to select types for which CollElems::NonPrimitive is used to store elements as Vec<ConstantVal>
 pub trait StoredNonPrimitive {}
 
@@ -137,6 +150,7 @@ impl StoredNonPrimitive for bool {}
 impl StoredNonPrimitive for i16 {}
 impl StoredNonPrimitive for i32 {}
 impl StoredNonPrimitive for i64 {}
+impl StoredNonPrimitive for ErgoBox {}
 
 impl<T: LiftIntoSType + StoredNonPrimitive + Into<Value>> Into<Value> for Vec<T> {
     fn into(self) -> Value {
@@ -219,6 +233,33 @@ impl TryExtractFrom<Value> for SigmaProp {
     }
 }
 
+impl TryExtractFrom<Value> for ErgoBox {
+    fn try_extract_from(c: Value) -> Result<Self, TryExtractFromError> {
+        match c {
+            Value::CBox(b) => Ok(*b),
+            _ => Err(TryExtractFromError(format!(
+                "expected ErgoBox, found {:?}",
+                c
+            ))),
+        }
+    }
+}
+
+impl<T: TryExtractFrom<Value> + StoredNonPrimitive> TryExtractFrom<Value> for Vec<T> {
+    fn try_extract_from(c: Value) -> Result<Self, TryExtractFromError> {
+        match c {
+            Value::Coll(Coll::NonPrimitive { elem_tpe: _, v }) => {
+                v.into_iter().map(T::try_extract_from).collect()
+            }
+            _ => Err(TryExtractFromError(format!(
+                "expected {:?}, found {:?}",
+                std::any::type_name::<Self>(),
+                c
+            ))),
+        }
+    }
+}
+
 impl TryFrom<Value> for ProveDlog {
     type Error = TryExtractFromError;
     fn try_from(cv: Value) -> Result<Self, Self::Error> {
@@ -239,3 +280,15 @@ impl TryFrom<Value> for ProveDlog {
         }
     }
 }
+
+impl TryExtractFrom<Value> for Rc<Context> {
+    fn try_extract_from(v: Value) -> Result<Self, TryExtractFromError> {
+        match v {
+            Value::Context(ctx) => Ok(ctx),
+            _ => Err(TryExtractFromError(format!(
+                "expected Context, found {:?}",
+                v
+            ))),
+        }
+    }
+}
diff --git a/ergo-lib/src/chain/ergo_box.rs b/ergo-lib/src/chain/ergo_box.rs
index 36f326e9f..66038da09 100644
--- a/ergo-lib/src/chain/ergo_box.rs
+++ b/ergo-lib/src/chain/ergo_box.rs
@@ -301,7 +301,7 @@ impl SigmaSerializable for ErgoBox {
 
 /// Contains the same fields as `ErgoBox`, except if transaction id and index,
 /// that will be calculated after full transaction formation.
-/// Use [`ErgoBoxCandidateBuilder`] to create an instance.
+/// Use [`box_builder::ErgoBoxCandidateBuilder`] to create an instance.
 #[derive(PartialEq, Eq, Clone, Debug)]
 pub struct ErgoBoxCandidate {
     /// amount of money associated with the box
diff --git a/ergo-lib/src/chain/ergo_box/register.rs b/ergo-lib/src/chain/ergo_box/register.rs
index d95b4ed6c..e7c07d891 100644
--- a/ergo-lib/src/chain/ergo_box/register.rs
+++ b/ergo-lib/src/chain/ergo_box/register.rs
@@ -46,7 +46,7 @@ impl NonMandatoryRegisterId {
     ];
 
     /// get register by it's index starting from 0
-    /// `i` is expected to be in range 0..[`NUM_REGS`] , otherwise panic
+    /// `i` is expected to be in range 0..[`Self::NUM_REGS`] , otherwise panic
     pub fn get_by_zero_index(i: usize) -> NonMandatoryRegisterId {
         assert!(i < NonMandatoryRegisterId::NUM_REGS);
         NonMandatoryRegisterId::REG_IDS[i]
diff --git a/ergo-lib/src/chain/ergo_state_context.rs b/ergo-lib/src/chain/ergo_state_context.rs
index 3764fd2d4..3b42b709c 100644
--- a/ergo-lib/src/chain/ergo_state_context.rs
+++ b/ergo-lib/src/chain/ergo_state_context.rs
@@ -2,11 +2,32 @@
 
 /// Blockchain state (last headers, etc.)
 #[derive(PartialEq, Eq, Debug, Clone)]
-pub struct ErgoStateContext();
+pub struct ErgoStateContext {
+    /// Block header with the current `spendingTransaction`, that can be predicted
+    /// by a miner before it's formation
+    pub pre_header: PreHeader,
+}
 
 impl ErgoStateContext {
-    /// Empty(dummy) value
+    /// Dummy instance intended for tests where actual values are not used
     pub fn dummy() -> ErgoStateContext {
-        ErgoStateContext()
+        ErgoStateContext {
+            pre_header: PreHeader::dummy(),
+        }
+    }
+}
+
+/// Block header with the current `spendingTransaction`, that can be predicted
+/// by a miner before it's formation
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct PreHeader {
+    /// Block height
+    pub height: i32,
+}
+
+impl PreHeader {
+    /// Dummy instance intended for tests where actual values are not used
+    pub fn dummy() -> Self {
+        PreHeader { height: 0 }
     }
 }
diff --git a/ergo-lib/src/chain/transaction/unsigned.rs b/ergo-lib/src/chain/transaction/unsigned.rs
index a9cba0c84..a7a84ad39 100644
--- a/ergo-lib/src/chain/transaction/unsigned.rs
+++ b/ergo-lib/src/chain/transaction/unsigned.rs
@@ -71,7 +71,7 @@ impl UnsignedTransaction {
         self.tx_id.clone()
     }
 
-    /// message to be signed by the [`Prover`] (serialized tx)
+    /// message to be signed by the [`crate::sigma_protocol::prover::Prover`] (serialized tx)
     pub fn bytes_to_sign(&self) -> Vec<u8> {
         let empty_proofs_input = self
             .inputs
diff --git a/ergo-lib/src/eval.rs b/ergo-lib/src/eval.rs
index 020c50ee2..8675caffd 100644
--- a/ergo-lib/src/eval.rs
+++ b/ergo-lib/src/eval.rs
@@ -1,17 +1,25 @@
 //! Interpreter
-use crate::ast::constant::Constant;
+use std::rc::Rc;
+
+use crate::ast::constant::TryExtractFromError;
 use crate::ast::expr::Expr;
 use crate::ast::value::Value;
-use crate::{sigma_protocol::sigma_boolean::SigmaBoolean, types::stype::SType};
+use crate::sigma_protocol::sigma_boolean::SigmaBoolean;
 
 use cost_accum::CostAccumulator;
 use thiserror::Error;
 
 use self::context::Context;
+use self::cost_accum::CostError;
+
+mod costs;
 
 pub(crate) mod context;
 pub(crate) mod cost_accum;
-mod costs;
+pub(crate) mod expr;
+pub(crate) mod global_vars;
+pub(crate) mod method_call;
+pub(crate) mod property_call;
 
 /// Environment for the interpreter
 pub struct Env();
@@ -33,6 +41,12 @@ pub enum EvalError {
     #[error("Unsupported Expr encountered during the evaluation")]
     // TODO: store unexpected expr
     UnexpectedExpr,
+    /// Error on cost calculation
+    #[error("Error on cost calculation: {0:?}")]
+    CostError(#[from] CostError),
+    /// Unexpected value type
+    #[error("Unexpected value type: {0:?}")]
+    TryExtractFrom(#[from] TryExtractFromError),
 }
 
 /// Result of expression reduction procedure (see `reduce_to_crypto`).
@@ -50,28 +64,35 @@ pub trait Evaluator {
         &self,
         expr: &Expr,
         env: &Env,
-        ctx: &Context,
+        ctx: Rc<Context>,
     ) -> Result<ReductionResult, EvalError> {
-        let mut ca = CostAccumulator::new(0, None);
-        eval(expr, env, &mut ca, ctx).and_then(|v| -> Result<ReductionResult, EvalError> {
-            match v {
-                Constant {
-                    tpe: SType::SBoolean,
-                    v: Value::Boolean(b),
-                } => Ok(ReductionResult {
-                    sigma_prop: SigmaBoolean::TrivialProp(b),
-                    cost: 0,
-                }),
-                Constant {
-                    tpe: SType::SSigmaProp,
-                    v: Value::SigmaProp(sp),
-                } => Ok(ReductionResult {
-                    sigma_prop: sp.value().clone(),
-                    cost: 0,
-                }),
-                _ => Err(EvalError::InvalidResultType),
-            }
-        })
+        let cost_accum = CostAccumulator::new(0, None);
+        let mut ectx = EvalContext::new(ctx, cost_accum);
+        expr.eval(env, &mut ectx)
+            .and_then(|v| -> Result<ReductionResult, EvalError> {
+                match v {
+                    Value::Boolean(b) => Ok(ReductionResult {
+                        sigma_prop: SigmaBoolean::TrivialProp(b),
+                        cost: 0,
+                    }),
+                    Value::SigmaProp(sp) => Ok(ReductionResult {
+                        sigma_prop: sp.value().clone(),
+                        cost: 0,
+                    }),
+                    _ => Err(EvalError::InvalidResultType),
+                }
+            })
+    }
+}
+
+pub struct EvalContext {
+    ctx: Rc<Context>,
+    cost_accum: CostAccumulator,
+}
+
+impl EvalContext {
+    pub fn new(ctx: Rc<Context>, cost_accum: CostAccumulator) -> Self {
+        EvalContext { ctx, cost_accum }
     }
 }
 
@@ -79,58 +100,54 @@ pub trait Evaluator {
 /// Should be implemented by every node that can be evaluated.
 pub trait Evaluable {
     /// Evaluation routine to be implement by each node
-    fn eval(
-        &self,
-        env: &Env,
-        ca: &mut CostAccumulator,
-        ctx: &Context,
-    ) -> Result<Constant, EvalError>;
+    fn eval(&self, env: &Env, ctx: &mut EvalContext) -> Result<Value, EvalError>;
 }
 
-#[allow(unconditional_recursion)]
-fn eval(
-    expr: &Expr,
-    env: &Env,
-    ca: &mut CostAccumulator,
-    ctx: &Context,
-) -> Result<Constant, EvalError> {
-    match expr {
-        Expr::Const(c) => Ok(c.clone()),
-        Expr::Coll { .. } => todo!(),
-        Expr::Tup { .. } => todo!(),
-        Expr::PredefFunc(_) => todo!(),
-        Expr::CollM(_) => todo!(),
-        Expr::BoxM(_) => todo!(),
-        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)?;
-            let _v_r = eval(r, env, ca, ctx)?;
-            ca.add_cost_of(expr);
-            todo!()
-            // Ok(match bin_op {
-            //     BinOp::Num(op) => match op {
-            //         NumOp::Add => v_l + v_r,
-            //     },
-            // })
-        }
-        _ => Err(EvalError::UnexpectedExpr),
-    }
-}
+// #[allow(unconditional_recursion)]
+// fn eval(
+//     expr: &Expr,
+//     env: &Env,
+//     ca: &mut CostAccumulator,
+//     ctx: &Context,
+// ) -> Result<Value, EvalError> {
+//     match expr {
+//         Expr::Const(c) => Ok(c.v.clone()),
+//         Expr::Coll { .. } => todo!(),
+//         Expr::Tup { .. } => todo!(),
+//         Expr::PredefFunc(_) => todo!(),
+//         Expr::CollM(_) => todo!(),
+//         Expr::BoxM(_) => todo!(),
+//         Expr::GlobalVars(v) => v.eval(env, ca, ctx),
+//         Expr::MethodCall(v) => v.eval(env, ca, ctx),
+//         Expr::BinOp(_bin_op, l, r) => {
+//             let _v_l = eval(l, env, ca, ctx)?;
+//             let _v_r = eval(r, env, ca, ctx)?;
+//             ca.add_cost_of(expr);
+//             todo!()
+//             // Ok(match bin_op {
+//             //     BinOp::Num(op) => match op {
+//             //         NumOp::Add => v_l + v_r,
+//             //     },
+//             // })
+//         }
+//         _ => Err(EvalError::UnexpectedExpr),
+//     }
+// }
 
 #[cfg(test)]
-mod tests {
+pub mod tests {
 
     use crate::ast::constant::TryExtractFrom;
-    use crate::ast::context_methods::ContextM;
 
     use super::*;
 
-    #[test]
-    fn 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);
+    pub fn eval_out<T: TryExtractFrom<Value>>(expr: &Expr, ctx: Rc<Context>) -> T {
+        use crate::ast::constant::TryExtractInto;
+        let cost_accum = CostAccumulator::new(0, None);
+        let mut ectx = EvalContext::new(ctx, cost_accum);
+        expr.eval(&Env::empty(), &mut ectx)
+            .unwrap()
+            .try_extract_into::<T>()
+            .unwrap()
     }
 }
diff --git a/ergo-lib/src/eval/context.rs b/ergo-lib/src/eval/context.rs
index 76ebc24ae..ecc143695 100644
--- a/ergo-lib/src/eval/context.rs
+++ b/ergo-lib/src/eval/context.rs
@@ -1,28 +1,92 @@
-use crate::ast::constant::Constant;
+use crate::chain::ergo_box::ErgoBox;
 use crate::chain::ergo_state_context::ErgoStateContext;
 use crate::wallet::signing::TransactionContext;
+use thiserror::Error;
 
 #[derive(PartialEq, Eq, Debug, Clone)]
 pub struct Context {
-    pub height: Constant,
+    pub height: i32,
+    pub self_box: ErgoBox,
+    pub outputs: Vec<ErgoBox>,
+    pub data_inputs: Vec<ErgoBox>,
 }
 
 impl Context {
+    /// Dummy instance intended for tests where actual values are not used
     #[cfg(test)]
     pub fn dummy() -> Self {
+        use crate::test_util::force_any_val;
         Context {
-            height: 0i32.into(),
+            height: 0,
+            self_box: force_any_val::<ErgoBox>(),
+            outputs: vec![force_any_val::<ErgoBox>()],
+            data_inputs: vec![],
         }
     }
 
+    /// Create new instance:
+    /// `self_index` - index of the SELF box in the tx_ctx.boxes_to_spend
     pub fn new(
-        _state_ctx: &ErgoStateContext,
-        _tx_ctx: &TransactionContext,
-        _self_index: usize,
-    ) -> Self {
-        // TODO: implement
-        Context {
-            height: 0i32.into(),
+        state_ctx: &ErgoStateContext,
+        tx_ctx: &TransactionContext,
+        self_index: usize,
+    ) -> Result<Self, ContextError> {
+        let height = state_ctx.pre_header.height;
+        let self_box = tx_ctx
+            .boxes_to_spend
+            .get(self_index)
+            .cloned()
+            .ok_or(ContextError::SelfIndexOutOfBounds)?;
+        let outputs: Vec<ErgoBox> = tx_ctx
+            .spending_tx
+            .output_candidates
+            .iter()
+            .enumerate()
+            .map(|(idx, b)| ErgoBox::from_box_candidate(b, tx_ctx.spending_tx.id(), idx as u16))
+            .collect();
+        let data_inputs: Vec<ErgoBox> = tx_ctx.data_boxes.clone();
+        Ok(Context {
+            height,
+            self_box,
+            outputs,
+            data_inputs,
+        })
+    }
+}
+
+#[derive(Error, PartialEq, Eq, Debug, Clone)]
+pub enum ContextError {
+    /// self_index is out of bounds for TransactionContext::boxes_to_spend
+    #[error("self_index is out of bounds for TransactionContext::boxes_to_spend")]
+    SelfIndexOutOfBounds,
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use proptest::collection::vec;
+    use proptest::prelude::*;
+
+    impl Arbitrary for Context {
+        type Parameters = ();
+
+        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
+            (
+                0..i32::MAX,
+                any::<ErgoBox>(),
+                vec(any::<ErgoBox>(), 0..3),
+                vec(any::<ErgoBox>(), 0..3),
+            )
+                .prop_map(|(height, self_box, outputs, data_inputs)| Self {
+                    height,
+                    self_box,
+                    outputs,
+                    data_inputs,
+                })
+                .boxed()
         }
+
+        type Strategy = BoxedStrategy<Self>;
     }
 }
diff --git a/ergo-lib/src/eval/cost_accum.rs b/ergo-lib/src/eval/cost_accum.rs
index 523866c82..8f476ec0c 100644
--- a/ergo-lib/src/eval/cost_accum.rs
+++ b/ergo-lib/src/eval/cost_accum.rs
@@ -1,21 +1,40 @@
 use super::costs::{Cost, Costs};
 use crate::ast::expr::Expr;
+use thiserror::Error;
 
 pub struct CostAccumulator {
     costs: Costs,
+    accum: u64,
+    limit: Option<u64>,
+}
+
+#[derive(Error, PartialEq, Eq, Debug, Clone)]
+pub enum CostError {
+    #[error("Limit ({0}) exceeded")]
+    LimitExceeded(u64),
 }
 
 impl CostAccumulator {
-    pub fn new(_initial_cost: u64, _cost_limit: Option<u64>) -> CostAccumulator {
+    pub fn new(initial_cost: u64, cost_limit: Option<u64>) -> CostAccumulator {
         CostAccumulator {
             costs: Costs::DEFAULT,
+            accum: initial_cost,
+            limit: cost_limit,
         }
     }
 
-    pub fn add_cost_of(&mut self, expr: &Expr) {
+    pub fn add_cost_of(&mut self, expr: &Expr) -> Result<(), CostError> {
         let cost = self.costs.cost_of(expr);
-        self.add(cost);
+        self.add(cost)
     }
 
-    pub fn add(&self, _: Cost) {}
+    pub fn add(&mut self, cost: Cost) -> Result<(), CostError> {
+        self.accum += u32::from(cost) as u64;
+        if let Some(limit) = self.limit {
+            if self.accum > limit {
+                return Err(CostError::LimitExceeded(limit));
+            }
+        }
+        Ok(())
+    }
 }
diff --git a/ergo-lib/src/eval/costs.rs b/ergo-lib/src/eval/costs.rs
index ec36279a4..d019394e6 100644
--- a/ergo-lib/src/eval/costs.rs
+++ b/ergo-lib/src/eval/costs.rs
@@ -1,5 +1,9 @@
 use crate::ast::expr::Expr;
 
+extern crate derive_more;
+use derive_more::{From, Into};
+
+#[derive(PartialEq, Eq, Debug, Clone, From, Into)]
 pub struct Cost(u32);
 
 pub struct Costs {}
@@ -10,6 +14,6 @@ impl Costs {
 
 impl Costs {
     pub fn cost_of(&self, _: &Expr) -> Cost {
-        todo!()
+        Cost(1)
     }
 }
diff --git a/ergo-lib/src/eval/expr.rs b/ergo-lib/src/eval/expr.rs
new file mode 100644
index 000000000..4400024a4
--- /dev/null
+++ b/ergo-lib/src/eval/expr.rs
@@ -0,0 +1,35 @@
+use crate::ast::expr::Expr;
+use crate::ast::value::Value;
+
+use super::Env;
+use super::EvalContext;
+use super::EvalError;
+use super::Evaluable;
+
+impl Evaluable for Expr {
+    fn eval(&self, env: &Env, ectx: &mut EvalContext) -> Result<Value, EvalError> {
+        ectx.cost_accum.add_cost_of(self)?;
+        match self {
+            Expr::Const(c) => Ok(c.v.clone()),
+            Expr::PredefFunc(_) => todo!(),
+            Expr::CollM(_) => todo!(),
+            Expr::BoxM(_) => todo!(),
+            Expr::GlobalVars(v) => v.eval(env, ectx),
+            Expr::MethodCall(v) => v.eval(env, ectx),
+            Expr::ProperyCall(v) => v.eval(env, ectx),
+            Expr::BinOp(_bin_op, _l, _r) => {
+                todo!()
+                // let _v_l = eval(l, env, ca, ctx)?;
+                // let _v_r = eval(r, env, ca, ctx)?;
+                // ca.add_cost_of(expr);
+                // Ok(match bin_op {
+                //     BinOp::Num(op) => match op {
+                //         NumOp::Add => v_l + v_r,
+                //     },
+                // })
+            }
+            Expr::Context => Ok(Value::Context(ectx.ctx.clone())),
+            _ => Err(EvalError::UnexpectedExpr),
+        }
+    }
+}
diff --git a/ergo-lib/src/eval/global_vars.rs b/ergo-lib/src/eval/global_vars.rs
new file mode 100644
index 000000000..cb4edfe23
--- /dev/null
+++ b/ergo-lib/src/eval/global_vars.rs
@@ -0,0 +1,57 @@
+use crate::ast::global_vars::GlobalVars;
+use crate::ast::value::Value;
+use crate::eval::Env;
+
+use super::EvalContext;
+use super::EvalError;
+use super::Evaluable;
+
+impl Evaluable for GlobalVars {
+    fn eval(&self, _env: &Env, ectx: &mut EvalContext) -> Result<Value, EvalError> {
+        match self {
+            GlobalVars::Height => Ok(ectx.ctx.height.clone().into()),
+            GlobalVars::SelfBox => Ok(ectx.ctx.self_box.clone().into()),
+            GlobalVars::Outputs => Ok(ectx.ctx.outputs.clone().into()),
+            _ => Err(EvalError::UnexpectedExpr),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::rc::Rc;
+
+    use crate::chain::ergo_box::ErgoBox;
+    use crate::eval::context::Context;
+    use crate::eval::tests::eval_out;
+    use crate::test_util::force_any_val;
+
+    use super::*;
+
+    #[test]
+    fn eval_height() {
+        let ctx = Rc::new(force_any_val::<Context>());
+        assert_eq!(
+            eval_out::<i32>(&GlobalVars::Height.into(), ctx.clone()),
+            ctx.height
+        );
+    }
+
+    #[test]
+    fn eval_self_box() {
+        let ctx = Rc::new(force_any_val::<Context>());
+        assert_eq!(
+            eval_out::<ErgoBox>(&GlobalVars::SelfBox.into(), ctx.clone()),
+            ctx.self_box
+        );
+    }
+
+    #[test]
+    fn eval_outputs() {
+        let ctx = Rc::new(force_any_val::<Context>());
+        assert_eq!(
+            eval_out::<Vec<ErgoBox>>(&GlobalVars::Outputs.into(), ctx.clone()),
+            ctx.outputs
+        );
+    }
+}
diff --git a/ergo-lib/src/eval/method_call.rs b/ergo-lib/src/eval/method_call.rs
new file mode 100644
index 000000000..f1f650a89
--- /dev/null
+++ b/ergo-lib/src/eval/method_call.rs
@@ -0,0 +1,45 @@
+use crate::ast::method_call::MethodCall;
+use crate::ast::value::Value;
+
+use super::Env;
+use super::EvalContext;
+use super::EvalError;
+use super::Evaluable;
+
+impl Evaluable for MethodCall {
+    fn eval(&self, env: &Env, ectx: &mut EvalContext) -> Result<Value, EvalError> {
+        let ov = (*self.obj).eval(env, ectx)?;
+        let argsv: Result<Vec<Value>, EvalError> =
+            self.args.iter().map(|arg| arg.eval(env, ectx)).collect();
+        self.method.eval_fn()(ov, argsv?)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    // use std::rc::Rc;
+
+    // use crate::ast::expr::Expr;
+    // use crate::chain::ergo_box::ErgoBox;
+    // use crate::eval::context::Context;
+    // use crate::eval::tests::eval_out;
+    // use crate::test_util::force_any_val;
+    // use crate::types::scontext;
+
+    // use super::*;
+
+    // #[test]
+    // fn eval_context_data_inputs() {
+    //     let mc = MethodCall {
+    //         tpe: scontext::DATA_INPUTS_METHOD.tpe().clone(),
+    //         obj: Box::new(Expr::Context),
+    //         method: scontext::DATA_INPUTS_METHOD.clone(),
+    //         args: vec![],
+    //     };
+    //     let ctx = Rc::new(force_any_val::<Context>());
+    //     assert_eq!(
+    //         eval_out::<Vec<ErgoBox>>(&mc.into(), ctx.clone()),
+    //         ctx.data_inputs
+    //     );
+    // }
+}
diff --git a/ergo-lib/src/eval/property_call.rs b/ergo-lib/src/eval/property_call.rs
new file mode 100644
index 000000000..0c1e1ca9b
--- /dev/null
+++ b/ergo-lib/src/eval/property_call.rs
@@ -0,0 +1,41 @@
+use crate::ast::property_call::PropertyCall;
+use crate::ast::value::Value;
+
+use super::Env;
+use super::EvalContext;
+use super::EvalError;
+use super::Evaluable;
+
+impl Evaluable for PropertyCall {
+    fn eval(&self, env: &Env, ectx: &mut EvalContext) -> Result<Value, EvalError> {
+        let ov = (*self.obj).eval(env, ectx)?;
+        self.method.eval_fn()(ov, vec![])
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::rc::Rc;
+
+    use crate::ast::expr::Expr;
+    use crate::chain::ergo_box::ErgoBox;
+    use crate::eval::context::Context;
+    use crate::eval::tests::eval_out;
+    use crate::test_util::force_any_val;
+    use crate::types::scontext;
+
+    use super::*;
+
+    #[test]
+    fn eval_context_data_inputs() {
+        let pc = PropertyCall {
+            obj: Box::new(Expr::Context),
+            method: scontext::DATA_INPUTS_PROPERTY.clone(),
+        };
+        let ctx = Rc::new(force_any_val::<Context>());
+        assert_eq!(
+            eval_out::<Vec<ErgoBox>>(&pc.into(), ctx.clone()),
+            ctx.data_inputs
+        );
+    }
+}
diff --git a/ergo-lib/src/lib.rs b/ergo-lib/src/lib.rs
index d67602bf9..dcb4aa767 100644
--- a/ergo-lib/src/lib.rs
+++ b/ergo-lib/src/lib.rs
@@ -11,6 +11,7 @@
 #![deny(missing_docs)]
 // Clippy exclusions
 #![allow(clippy::unit_arg)]
+#![deny(broken_intra_doc_links)]
 
 mod big_integer;
 mod eval;
diff --git a/ergo-lib/src/serialization.rs b/ergo-lib/src/serialization.rs
index ed3450b94..3a4384093 100644
--- a/ergo-lib/src/serialization.rs
+++ b/ergo-lib/src/serialization.rs
@@ -5,6 +5,9 @@ mod constant_placeholder;
 mod data;
 mod expr;
 mod fold;
+mod global_vars;
+mod method_call;
+mod property_call;
 mod sigmaboolean;
 
 pub(crate) mod constant_store;
diff --git a/ergo-lib/src/serialization/data.rs b/ergo-lib/src/serialization/data.rs
index 6ae1dec49..d5fe841b3 100644
--- a/ergo-lib/src/serialization/data.rs
+++ b/ergo-lib/src/serialization/data.rs
@@ -23,11 +23,13 @@ impl DataSerializer {
             Value::Byte(v) => w.put_i8(*v),
             Value::Short(v) => w.put_i16(*v),
             Value::Int(v) => w.put_i32(*v),
+            // Value::TInt(v) => w.put_i32(v.raw),
             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::TBox(_) => todo!(),
             Value::AvlTree => todo!(),
             Value::Coll(ct) => match ct {
                 Coll::Primitive(CollPrim::CollByte(b)) => {
@@ -41,6 +43,7 @@ impl DataSerializer {
                 }
             },
             Value::Tup(_) => todo!(),
+            Value::Context(_) => todo!(), // TODO: throw error? it should not be here
         }
     }
 
diff --git a/ergo-lib/src/serialization/expr.rs b/ergo-lib/src/serialization/expr.rs
index bb2ae1701..bb4eb02da 100644
--- a/ergo-lib/src/serialization/expr.rs
+++ b/ergo-lib/src/serialization/expr.rs
@@ -3,6 +3,9 @@ use crate::ast::coll_methods::CollM;
 use crate::ast::constant::Constant;
 use crate::ast::constant::ConstantPlaceholder;
 use crate::ast::expr::Expr;
+use crate::ast::global_vars::GlobalVars;
+use crate::ast::method_call::MethodCall;
+use crate::ast::property_call::PropertyCall;
 use crate::serialization::{
     sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable,
 };
@@ -28,6 +31,10 @@ impl SigmaSerializable for Expr {
                         CollM::Fold { .. } => FoldSerializer::sigma_serialize(expr, w),
                     },
                     Expr::ConstPlaceholder(cp) => cp.sigma_serialize(w),
+                    Expr::GlobalVars(_) => Ok(()),
+                    Expr::MethodCall(mc) => mc.sigma_serialize(w),
+                    Expr::ProperyCall(pc) => pc.sigma_serialize(w),
+                    Expr::Context => Ok(()),
                     _ => panic!(format!("don't know how to serialize {:?}", expr)),
                 }
             }
@@ -61,6 +68,13 @@ impl SigmaSerializable for Expr {
                         Ok(Expr::ConstPlaceholder(cp))
                     }
                 }
+                OpCode::HEIGHT => Ok(Expr::GlobalVars(GlobalVars::Height)),
+                OpCode::SELF_BOX => Ok(Expr::GlobalVars(GlobalVars::SelfBox)),
+                OpCode::INPUTS => Ok(Expr::GlobalVars(GlobalVars::Inputs)),
+                OpCode::OUTPUTS => Ok(Expr::GlobalVars(GlobalVars::Outputs)),
+                OpCode::PROPERTY_CALL => Ok(Expr::ProperyCall(PropertyCall::sigma_parse(r)?)),
+                OpCode::METHOD_CALL => Ok(Expr::MethodCall(MethodCall::sigma_parse(r)?)),
+                OpCode::CONTEXT => Ok(Expr::Context),
                 o => Err(SerializationError::NotImplementedOpCode(o.value())),
             }
         }
diff --git a/ergo-lib/src/serialization/global_vars.rs b/ergo-lib/src/serialization/global_vars.rs
new file mode 100644
index 000000000..83498be41
--- /dev/null
+++ b/ergo-lib/src/serialization/global_vars.rs
@@ -0,0 +1,17 @@
+#[cfg(test)]
+mod tests {
+    use crate::ast::expr::Expr;
+    use crate::ast::global_vars::GlobalVars;
+    use crate::serialization::sigma_serialize_roundtrip;
+
+    use proptest::prelude::*;
+
+    proptest! {
+
+        #[test]
+        fn ser_roundtrip(v in any::<GlobalVars>()) {
+            let expr = Expr::GlobalVars(v);
+            prop_assert_eq![sigma_serialize_roundtrip(&expr), expr];
+        }
+    }
+}
diff --git a/ergo-lib/src/serialization/method_call.rs b/ergo-lib/src/serialization/method_call.rs
new file mode 100644
index 000000000..f3faf31d4
--- /dev/null
+++ b/ergo-lib/src/serialization/method_call.rs
@@ -0,0 +1,59 @@
+use std::io::Error;
+
+use crate::ast::expr::Expr;
+use crate::ast::method_call::MethodCall;
+use crate::types::smethod::MethodId;
+use crate::types::smethod::SMethod;
+use crate::types::stype_companion::TypeId;
+
+use super::sigma_byte_reader::SigmaByteRead;
+use super::sigma_byte_writer::SigmaByteWrite;
+use super::SerializationError;
+use super::SigmaSerializable;
+
+impl SigmaSerializable for MethodCall {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), Error> {
+        self.method.obj_type.type_id().sigma_serialize(w)?;
+        self.method.method_id().sigma_serialize(w)?;
+        self.obj.sigma_serialize(w)?;
+        w.put_u32(self.args.len() as u32)?;
+        self.args.iter().try_for_each(|a| a.sigma_serialize(w))?;
+        Ok(())
+    }
+
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
+        let type_id = TypeId::sigma_parse(r)?;
+        let method_id = MethodId::sigma_parse(r)?;
+        let obj = Expr::sigma_parse(r)?;
+        let args_count = r.get_u32()?;
+        let mut args = Vec::with_capacity(args_count as usize);
+        for _ in 0..args_count {
+            args.push(Expr::sigma_parse(r)?);
+        }
+        Ok(MethodCall {
+            obj: Box::new(obj),
+            method: SMethod::from_ids(type_id, method_id),
+            args,
+        })
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    // use crate::ast::expr::Expr;
+    // use crate::ast::method_call::MethodCall;
+    // use crate::serialization::sigma_serialize_roundtrip;
+    // use crate::types::scontext;
+
+    // #[test]
+    // fn ser_roundtrip_property() {
+    //     let mc = MethodCall {
+    //         tpe: scontext::DATA_INPUTS_METHOD.tpe().clone(),
+    //         obj: Box::new(Expr::Context),
+    //         method: scontext::DATA_INPUTS_METHOD.clone(),
+    //         args: vec![],
+    //     };
+    //     let expr = Expr::MethodCall(mc);
+    //     assert_eq![sigma_serialize_roundtrip(&expr), expr];
+    // }
+}
diff --git a/ergo-lib/src/serialization/op_code.rs b/ergo-lib/src/serialization/op_code.rs
index bdd20040a..c610c0720 100644
--- a/ergo-lib/src/serialization/op_code.rs
+++ b/ergo-lib/src/serialization/op_code.rs
@@ -8,17 +8,32 @@ use sigma_ser::vlq_encode;
 use std::io;
 use vlq_encode::WriteSigmaVlqExt;
 
-#[derive(PartialEq, Eq, Hash, Copy, Clone)]
+#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
 pub struct OpCode(u8);
 
 impl OpCode {
+    // reference implementation
+    // https://github.com/ScorexFoundation/sigmastate-interpreter/blob/develop/sigmastate/src/main/scala/sigmastate/serialization/OpCodes.scala
+
     pub const LAST_DATA_TYPE: OpCode = OpCode(111);
     pub const LAST_CONSTANT_CODE: OpCode = OpCode(Self::LAST_DATA_TYPE.value() + 1);
 
     pub const CONSTANT_PLACEHOLDER: OpCode = Self::new_op_code(3);
+
+    /// Environment (context methods)
+    pub const HEIGHT: OpCode = Self::new_op_code(51);
+    pub const INPUTS: OpCode = Self::new_op_code(52);
+    pub const OUTPUTS: OpCode = Self::new_op_code(53);
+    pub const SELF_BOX: OpCode = Self::new_op_code(55);
+
     pub const FOLD: OpCode = Self::new_op_code(64);
     pub const PROVE_DLOG: OpCode = Self::new_op_code(93);
 
+    pub const PROPERTY_CALL: OpCode = Self::new_op_code(107);
+    pub const METHOD_CALL: OpCode = Self::new_op_code(108);
+
+    pub const CONTEXT: OpCode = Self::new_op_code(142);
+
     const fn new_op_code(shift: u8) -> OpCode {
         OpCode(Self::LAST_CONSTANT_CODE.value() + shift)
     }
diff --git a/ergo-lib/src/serialization/property_call.rs b/ergo-lib/src/serialization/property_call.rs
new file mode 100644
index 000000000..89a126353
--- /dev/null
+++ b/ergo-lib/src/serialization/property_call.rs
@@ -0,0 +1,49 @@
+use std::io::Error;
+
+use crate::ast::expr::Expr;
+use crate::ast::property_call::PropertyCall;
+use crate::types::smethod::MethodId;
+use crate::types::smethod::SMethod;
+use crate::types::stype_companion::TypeId;
+
+use super::sigma_byte_reader::SigmaByteRead;
+use super::sigma_byte_writer::SigmaByteWrite;
+use super::SerializationError;
+use super::SigmaSerializable;
+
+impl SigmaSerializable for PropertyCall {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), Error> {
+        self.method.obj_type.type_id().sigma_serialize(w)?;
+        self.method.method_id().sigma_serialize(w)?;
+        self.obj.sigma_serialize(w)?;
+        Ok(())
+    }
+
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
+        let type_id = TypeId::sigma_parse(r)?;
+        let method_id = MethodId::sigma_parse(r)?;
+        let obj = Expr::sigma_parse(r)?;
+        Ok(PropertyCall {
+            obj: Box::new(obj),
+            method: SMethod::from_ids(type_id, method_id),
+        })
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::ast::expr::Expr;
+    use crate::ast::property_call::PropertyCall;
+    use crate::serialization::sigma_serialize_roundtrip;
+    use crate::types::scontext;
+
+    #[test]
+    fn ser_roundtrip_property() {
+        let mc = PropertyCall {
+            obj: Box::new(Expr::Context),
+            method: scontext::DATA_INPUTS_PROPERTY.clone(),
+        };
+        let expr = Expr::ProperyCall(mc);
+        assert_eq![sigma_serialize_roundtrip(&expr), expr];
+    }
+}
diff --git a/ergo-lib/src/serialization/types.rs b/ergo-lib/src/serialization/types.rs
index 7be552f16..0c9bcf44b 100644
--- a/ergo-lib/src/serialization/types.rs
+++ b/ergo-lib/src/serialization/types.rs
@@ -128,6 +128,7 @@ impl SigmaSerializable for SType {
             SType::SColl(_) => todo!(),
             SType::STup(_) => todo!(),
             SType::SFunc(_) => todo!(),
+            SType::SContext(_) => todo!(),
         }
     }
 
diff --git a/ergo-lib/src/sigma_protocol/prover.rs b/ergo-lib/src/sigma_protocol/prover.rs
index 85a1d27ed..2b700fd76 100644
--- a/ergo-lib/src/sigma_protocol/prover.rs
+++ b/ergo-lib/src/sigma_protocol/prover.rs
@@ -3,6 +3,8 @@
 mod context_extension;
 mod prover_result;
 
+use std::rc::Rc;
+
 pub use context_extension::*;
 pub use prover_result::*;
 
@@ -68,7 +70,7 @@ pub trait Prover: Evaluator {
         &self,
         tree: &ErgoTree,
         env: &Env,
-        ctx: &Context,
+        ctx: Rc<Context>,
         message: &[u8],
     ) -> Result<ProverResult, ProverError> {
         let expr = tree.proposition()?;
@@ -319,7 +321,7 @@ mod tests {
         let res = prover.prove(
             &bool_true_tree,
             &Env::empty(),
-            &Context::dummy(),
+            Rc::new(Context::dummy()),
             message.as_slice(),
         );
         assert!(res.is_ok());
@@ -338,7 +340,7 @@ mod tests {
         let res = prover.prove(
             &bool_false_tree,
             &Env::empty(),
-            &Context::dummy(),
+            Rc::new(Context::dummy()),
             message.as_slice(),
         );
         assert!(res.is_err());
@@ -358,7 +360,12 @@ mod tests {
         let prover = TestProver {
             secrets: vec![PrivateInput::DlogProverInput(secret)],
         };
-        let res = prover.prove(&tree, &Env::empty(), &Context::dummy(), message.as_slice());
+        let res = prover.prove(
+            &tree,
+            &Env::empty(),
+            Rc::new(Context::dummy()),
+            message.as_slice(),
+        );
         assert!(res.is_ok());
         assert_ne!(res.unwrap().proof, ProofBytes::Empty);
     }
diff --git a/ergo-lib/src/sigma_protocol/verifier.rs b/ergo-lib/src/sigma_protocol/verifier.rs
index 1171e0118..3f1b698dc 100644
--- a/ergo-lib/src/sigma_protocol/verifier.rs
+++ b/ergo-lib/src/sigma_protocol/verifier.rs
@@ -1,5 +1,7 @@
 //! Verifier
 
+use std::rc::Rc;
+
 use super::prover::ProofBytes;
 use super::{
     dlog_protocol,
@@ -42,7 +44,7 @@ pub struct VerificationResult {
     pub cost: u64,
 }
 
-/// Verifier for the proofs generater by [`Prover`]
+/// Verifier for the proofs generater by [`super::prover::Prover`]
 pub trait Verifier: Evaluator {
     /// Executes the script in a given context.
     /// Step 1: Deserialize context variables
@@ -52,7 +54,7 @@ pub trait Verifier: Evaluator {
         &self,
         tree: &ErgoTree,
         env: &Env,
-        ctx: &Context,
+        ctx: Rc<Context>,
         proof: &ProofBytes,
         message: &[u8],
     ) -> Result<VerificationResult, VerifierError> {
@@ -145,11 +147,11 @@ mod tests {
             let prover = TestProver {
                 secrets: vec![PrivateInput::DlogProverInput(secret)],
             };
-            let res = prover.prove(&tree, &Env::empty(), &Context::dummy(), message.as_slice());
+            let res = prover.prove(&tree, &Env::empty(), Rc::new(Context::dummy()), message.as_slice());
             let proof = res.unwrap().proof;
 
             let verifier = TestVerifier;
-            let ver_res = verifier.verify(&tree, &Env::empty(), &Context::dummy(),  &proof, message.as_slice());
+            let ver_res = verifier.verify(&tree, &Env::empty(), Rc::new(Context::dummy()),  &proof, message.as_slice());
             prop_assert_eq!(ver_res.unwrap().result, true);
         }
     }
@@ -248,7 +250,7 @@ mod tests {
         let ver_res = verifier.verify(
             &ergo_tree,
             &Env::empty(),
-            &Context::dummy(),
+            Rc::new(Context::dummy()),
             &tx.inputs.get(1).unwrap().spending_proof.proof,
             message.as_slice(),
         );
diff --git a/ergo-lib/src/types.rs b/ergo-lib/src/types.rs
index c7cfd173e..977e880f5 100644
--- a/ergo-lib/src/types.rs
+++ b/ergo-lib/src/types.rs
@@ -2,6 +2,7 @@
 
 pub mod stype;
 
+pub(crate) mod scontext;
 pub(crate) mod sfunc;
 pub(crate) mod smethod;
 pub(crate) mod stype_companion;
diff --git a/ergo-lib/src/types/scontext.rs b/ergo-lib/src/types/scontext.rs
new file mode 100644
index 000000000..040f1c00b
--- /dev/null
+++ b/ergo-lib/src/types/scontext.rs
@@ -0,0 +1,63 @@
+use std::rc::Rc;
+
+use crate::ast::constant::TryExtractInto;
+use crate::ast::value::Coll;
+use crate::ast::value::Value;
+use crate::eval::context::Context;
+
+use super::sfunc::SFunc;
+use super::smethod::EvalFn;
+use super::smethod::MethodId;
+use super::smethod::SMethod;
+use super::smethod::SMethodDesc;
+use super::stype::SType;
+use super::stype_companion::STypeCompanion;
+use super::stype_companion::STypeCompanionHead;
+use super::stype_companion::TypeId;
+use lazy_static::lazy_static;
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct SContext();
+
+static S_CONTEXT_TYPE_COMPANION_HEAD: STypeCompanionHead = STypeCompanionHead {
+    type_id: TypeId(108),
+    type_name: "Context",
+};
+
+static DATA_INPUTS_EVAL_FN: EvalFn = |obj, _args| {
+    Ok(Value::Coll(Coll::NonPrimitive {
+        v: obj
+            .try_extract_into::<Rc<Context>>()?
+            .data_inputs
+            .clone()
+            .into_iter()
+            .map(|b| Value::CBox(Box::new(b)))
+            .collect(),
+        elem_tpe: SType::SBox,
+    }))
+};
+
+lazy_static! {
+    static ref DATA_INPUTS_PROPERTY_RAW: SMethodDesc = SMethodDesc {
+        method_id: MethodId(1),
+        name: "dataInputs",
+        tpe: SType::SFunc(Box::new(SFunc {
+            t_dom: vec![SType::SContext(SContext())],
+            t_range: SType::SColl(Box::new(SType::SBox)),
+            tpe_params: vec![],
+        })),
+        eval_fn: DATA_INPUTS_EVAL_FN,
+    };
+}
+
+lazy_static! {
+    pub static ref S_CONTEXT_TYPE_COMPANION: STypeCompanion = STypeCompanion::new(
+        &S_CONTEXT_TYPE_COMPANION_HEAD,
+        vec![&DATA_INPUTS_PROPERTY_RAW]
+    );
+}
+
+lazy_static! {
+    pub static ref DATA_INPUTS_PROPERTY: SMethod =
+        SMethod::new(&S_CONTEXT_TYPE_COMPANION, &DATA_INPUTS_PROPERTY_RAW,);
+}
diff --git a/ergo-lib/src/types/sfunc.rs b/ergo-lib/src/types/sfunc.rs
index cd1c7095c..7a1c0b570 100644
--- a/ergo-lib/src/types/sfunc.rs
+++ b/ergo-lib/src/types/sfunc.rs
@@ -3,7 +3,7 @@ use super::stype_param::STypeParam;
 
 #[derive(PartialEq, Eq, Debug, Clone)]
 pub struct SFunc {
-    t_dom: Vec<SType>,
-    t_range: SType,
-    tpe_params: Vec<STypeParam>,
+    pub t_dom: Vec<SType>,
+    pub t_range: SType,
+    pub tpe_params: Vec<STypeParam>,
 }
diff --git a/ergo-lib/src/types/smethod.rs b/ergo-lib/src/types/smethod.rs
index f3314a2d0..48f232a2d 100644
--- a/ergo-lib/src/types/smethod.rs
+++ b/ergo-lib/src/types/smethod.rs
@@ -1,14 +1,81 @@
+use std::io::Error;
+
+use crate::ast::value::Value;
+use crate::eval::EvalError;
+use crate::serialization::sigma_byte_reader::SigmaByteRead;
+use crate::serialization::sigma_byte_writer::SigmaByteWrite;
+use crate::serialization::SerializationError;
+use crate::serialization::SigmaSerializable;
+
 use super::stype::SType;
 use super::stype_companion::STypeCompanion;
+use super::stype_companion::TypeId;
 
 /// Method id unique among the methods of the same object
 #[derive(PartialEq, Eq, Debug, Clone)]
-pub struct MethodId(u8);
+pub struct MethodId(pub u8);
+
+impl SigmaSerializable for MethodId {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), Error> {
+        w.put_u8(self.0)
+    }
+
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
+        Ok(Self(r.get_u8()?))
+    }
+}
 
 #[derive(PartialEq, Eq, Debug, Clone)]
 pub struct SMethod {
-    pub obj_type: Box<STypeCompanion>,
-    pub name: String,
+    pub obj_type: &'static STypeCompanion,
+    method_raw: &'static SMethodDesc,
+}
+
+impl SMethod {
+    pub fn new(obj_type: &'static STypeCompanion, method_raw: &'static SMethodDesc) -> SMethod {
+        SMethod {
+            obj_type,
+            method_raw,
+        }
+    }
+
+    pub fn from_ids(type_id: TypeId, method_id: MethodId) -> Self {
+        let obj_type = STypeCompanion::type_by_id(type_id);
+        obj_type.method_by_id(method_id).unwrap()
+    }
+
+    pub fn tpe(&self) -> &SType {
+        &self.method_raw.tpe
+    }
+
+    pub fn name(&self) -> &'static str {
+        self.method_raw.name
+    }
+
+    pub fn method_id(&self) -> MethodId {
+        self.method_raw.method_id.clone()
+    }
+
+    pub fn eval_fn(&self) -> EvalFn {
+        self.method_raw.eval_fn
+    }
+}
+
+pub type EvalFn = fn(Value, Vec<Value>) -> Result<Value, EvalError>;
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct SMethodDesc {
+    pub name: &'static str,
     pub method_id: MethodId,
     pub tpe: SType,
+    pub eval_fn: EvalFn,
+}
+
+impl SMethodDesc {
+    pub fn as_method(&'static self, obj_type: &'static STypeCompanion) -> SMethod {
+        SMethod {
+            obj_type,
+            method_raw: self,
+        }
+    }
 }
diff --git a/ergo-lib/src/types/stype.rs b/ergo-lib/src/types/stype.rs
index 7ec601c18..f8f2ae4ce 100644
--- a/ergo-lib/src/types/stype.rs
+++ b/ergo-lib/src/types/stype.rs
@@ -1,7 +1,14 @@
 //! SType hierarchy
 
+use crate::chain::ergo_box::ErgoBox;
 use crate::serialization::types::TypeCode;
+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 super::scontext::SContext;
 use super::sfunc::SFunc;
 use super::stype_companion::STypeCompanion;
 
@@ -38,6 +45,8 @@ pub enum SType {
     STup(Vec<SType>),
     /// Function (signature)
     SFunc(Box<SFunc>),
+    /// Context object ("CONTEXT" in ErgoScript)
+    SContext(SContext),
 }
 
 impl SType {
@@ -59,11 +68,12 @@ impl SType {
             SType::SColl(_) => todo!(),
             SType::STup(_) => todo!(),
             SType::SFunc(_) => todo!(),
+            SType::SContext(_) => todo!(),
         }
     }
 
     /// Get STypeCompanion instance associated with this SType
-    pub fn type_companion(&self) -> Option<STypeCompanion> {
+    pub fn type_companion(&self) -> Option<Box<STypeCompanion>> {
         todo!()
     }
 
@@ -115,6 +125,42 @@ impl LiftIntoSType for i64 {
     }
 }
 
+impl LiftIntoSType for ErgoBox {
+    fn stype() -> SType {
+        SType::SBox
+    }
+}
+
+impl LiftIntoSType for SigmaBoolean {
+    fn stype() -> SType {
+        SType::SSigmaProp
+    }
+}
+
+impl LiftIntoSType for SigmaProofOfKnowledgeTree {
+    fn stype() -> SType {
+        SType::SSigmaProp
+    }
+}
+
+impl LiftIntoSType for SigmaProp {
+    fn stype() -> SType {
+        SType::SSigmaProp
+    }
+}
+
+impl LiftIntoSType for ProveDlog {
+    fn stype() -> SType {
+        SType::SSigmaProp
+    }
+}
+
+impl LiftIntoSType for EcPoint {
+    fn stype() -> SType {
+        SType::SGroupElement
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/ergo-lib/src/types/stype_companion.rs b/ergo-lib/src/types/stype_companion.rs
index 99abd0fd1..53e6d087c 100644
--- a/ergo-lib/src/types/stype_companion.rs
+++ b/ergo-lib/src/types/stype_companion.rs
@@ -1,11 +1,70 @@
+use std::fmt::Debug;
+use std::io::Error;
+
+use crate::serialization::sigma_byte_reader::SigmaByteRead;
+use crate::serialization::sigma_byte_writer::SigmaByteWrite;
+use crate::serialization::SerializationError;
+use crate::serialization::SigmaSerializable;
+
+use super::scontext;
+use super::smethod::MethodId;
 use super::smethod::SMethod;
+use super::smethod::SMethodDesc;
+
+#[derive(PartialEq, Eq, Debug, Clone, Copy)]
+pub struct TypeId(pub u8);
+
+impl SigmaSerializable for TypeId {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), Error> {
+        w.put_u8(self.0)
+    }
+
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
+        Ok(Self(r.get_u8()?))
+    }
+}
 
 #[derive(PartialEq, Eq, Debug, Clone)]
-pub struct TypeId(u8);
+pub struct STypeCompanionHead {
+    pub type_id: TypeId,
+    pub type_name: &'static str,
+}
 
 #[derive(PartialEq, Eq, Debug, Clone)]
 pub struct STypeCompanion {
-    pub type_id: TypeId,
-    pub type_name: String,
-    pub methods: Vec<SMethod>,
+    head: &'static STypeCompanionHead,
+    methods: Vec<&'static SMethodDesc>,
+}
+
+impl STypeCompanion {
+    pub fn new(head: &'static STypeCompanionHead, methods: Vec<&'static SMethodDesc>) -> Self {
+        STypeCompanion { head, methods }
+    }
+
+    pub fn type_by_id(type_id: TypeId) -> &'static STypeCompanion {
+        if type_id == scontext::S_CONTEXT_TYPE_COMPANION.type_id() {
+            &scontext::S_CONTEXT_TYPE_COMPANION
+        } else {
+            todo!("cannot find STypeCompanion for {0:?} type id", type_id)
+        }
+    }
+
+    pub fn method_by_id(&'static self, method_id: MethodId) -> Option<SMethod> {
+        self.methods
+            .iter()
+            .find(|m| m.method_id == method_id)
+            .map(|m| m.as_method(self))
+    }
+
+    pub fn methods(&'static self) -> Vec<SMethod> {
+        self.methods.iter().map(|m| m.as_method(self)).collect()
+    }
+
+    pub fn type_id(&'static self) -> TypeId {
+        self.head.type_id
+    }
+
+    pub fn type_name(&'static self) -> &'static str {
+        self.head.type_name
+    }
 }
diff --git a/ergo-lib/src/wallet/signing.rs b/ergo-lib/src/wallet/signing.rs
index f0a548410..7c726a342 100644
--- a/ergo-lib/src/wallet/signing.rs
+++ b/ergo-lib/src/wallet/signing.rs
@@ -1,7 +1,10 @@
 //! Transaction signing
 
+use std::rc::Rc;
+
 use crate::chain::transaction::Input;
 use crate::eval::context::Context;
+use crate::eval::context::ContextError;
 use crate::{
     chain::{
         ergo_box::ErgoBox,
@@ -23,6 +26,9 @@ pub enum TxSigningError {
     /// failed to find an input in boxes_to_spend
     #[error("Input box not found (index {0})")]
     InputBoxNotFound(usize),
+    /// Context creation error
+    #[error("Context error: {0:?}")]
+    ContextError(#[from] ContextError),
 }
 
 /// Transaction and an additional info required for signing
@@ -51,12 +57,12 @@ pub fn sign_transaction(
         .enumerate()
         .try_for_each(|(idx, input_box)| {
             if let Some(unsigned_input) = tx.inputs.get(idx) {
-                let ctx = Context::new(state_context, &tx_context, idx);
+                let ctx = Rc::new(Context::new(state_context, &tx_context, idx)?);
                 prover
                     .prove(
                         &input_box.ergo_tree,
                         &Env::empty(),
-                        &ctx,
+                        ctx,
                         message_to_sign.as_slice(),
                     )
                     .map(|proof| {
@@ -114,7 +120,7 @@ mod tests {
                 let res = verifier.verify(
                     &b.ergo_tree,
                     &Env::empty(),
-                    &Context::dummy(),
+                    Rc::new(Context::dummy()),
                     &input.spending_proof.proof,
                     &message,
                 )?;
diff --git a/ergo-lib/src/wallet/tx_builder.rs b/ergo-lib/src/wallet/tx_builder.rs
index 33363a59a..2cafd8c3e 100644
--- a/ergo-lib/src/wallet/tx_builder.rs
+++ b/ergo-lib/src/wallet/tx_builder.rs
@@ -38,7 +38,7 @@ pub struct TxBuilder<S: ErgoBoxAssets> {
 
 impl<S: ErgoBoxAssets + ErgoBoxId + Clone> TxBuilder<S> {
     /// Creates new TxBuilder
-    /// `box_selection` - selected input boxes  (via [`BoxSelector`])
+    /// `box_selection` - selected input boxes  (via [`super::box_selector::BoxSelector`])
     /// `output_candidates` - output boxes to be "created" in this transaction,
     /// `current_height` - chain height that will be used in additionally created boxes (change, miner's fee, etc.),
     /// `fee_amount` - miner's fee (higher values will speed up inclusion in blocks),


Test patch:
diff --git a/.github/workflows/js_tests.yml b/.github/workflows/js_tests.yml
new file mode 100644
index 000000000..7ebdb15be
--- /dev/null
+++ b/.github/workflows/js_tests.yml
@@ -0,0 +1,34 @@
+name: JS tests
+on:
+  push:
+    branches:
+      - master
+      - develop
+  pull_request:
+    types:
+      - opened
+      - synchronize
+
+
+jobs:
+  js_tests:
+    name: run JS tests
+    runs-on: ubuntu-latest
+    defaults:
+      run: 
+        working-directory: bindings/ergo-lib-wasm
+    steps:
+      - name: checkout
+        uses: actions/checkout@v2
+
+      - name: install deps
+        run: |
+          curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f
+          curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
+          sudo apt-get -y install nodejs
+
+      - name: run tests
+        run: |
+          wasm-pack build
+          npm install && npm run test
+