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

Issue:
Convert `STypeCompanion` to enum
### Motivation
- To have exhaust compiler check in `ergotree_interpreter::eval::smethod_eval_fn`;

https://github.com/ergoplatform/sigma-rust/blob/6698880353fbec1038d0947ca2488746bcb77f34/ergotree-interpreter/src/eval.rs#L176-L203

- To have exhaust compiler check instead of `ergotree_ir::types::stype_companion::STypeCompanion::type_by_id()`

https://github.com/ergoplatform/sigma-rust/blob/6698880353fbec1038d0947ca2488746bcb77f34/ergotree-ir/src/types/stype_companion.rs#L37-L56



Discussion / hints:

Fix patch:
diff --git a/ergotree-interpreter/src/eval.rs b/ergotree-interpreter/src/eval.rs
index 0e94af76d..cb872e5ef 100644
--- a/ergotree-interpreter/src/eval.rs
+++ b/ergotree-interpreter/src/eval.rs
@@ -193,11 +193,11 @@ type EvalFn = fn(env: &Env, ctx: &mut EvalContext, Value, Vec<Value>) -> Result<
 
 fn smethod_eval_fn(method: &SMethod) -> Result<EvalFn, EvalError> {
     use ergotree_ir::types::*;
-    Ok(match method.obj_type.type_id() {
-        scontext::TYPE_ID if method.method_id() == scontext::DATA_INPUTS_PROPERTY_METHOD_ID => {
+    Ok(match method.obj_type.type_code() {
+        scontext::TYPE_CODE if method.method_id() == scontext::DATA_INPUTS_PROPERTY_METHOD_ID => {
             self::scontext::DATA_INPUTS_EVAL_FN
         }
-        sbox::TYPE_ID => match method.method_id() {
+        sbox::TYPE_CODE => match method.method_id() {
             sbox::VALUE_METHOD_ID => self::sbox::VALUE_EVAL_FN,
             sbox::GET_REG_METHOD_ID => self::sbox::GET_REG_EVAL_FN,
             sbox::TOKENS_METHOD_ID => self::sbox::TOKENS_EVAL_FN,
@@ -208,7 +208,7 @@ fn smethod_eval_fn(method: &SMethod) -> Result<EvalFn, EvalError> {
                 )))
             }
         },
-        scoll::TYPE_ID => match method.method_id() {
+        scoll::TYPE_CODE => match method.method_id() {
             scoll::INDEX_OF_METHOD_ID => self::scoll::INDEX_OF_EVAL_FN,
             scoll::FLATMAP_METHOD_ID => self::scoll::FLATMAP_EVAL_FN,
             scoll::ZIP_METHOD_ID => self::scoll::ZIP_EVAL_FN,
@@ -223,7 +223,7 @@ fn smethod_eval_fn(method: &SMethod) -> Result<EvalFn, EvalError> {
                 )))
             }
         },
-        sgroup_elem::TYPE_ID => match method.method_id() {
+        sgroup_elem::TYPE_CODE => match method.method_id() {
             sgroup_elem::GET_ENCODED_METHOD_ID => self::sgroup_elem::GET_ENCODED_EVAL_FN,
             sgroup_elem::NEGATE_METHOD_ID => self::sgroup_elem::NEGATE_EVAL_FN,
             method_id => {
@@ -233,7 +233,7 @@ fn smethod_eval_fn(method: &SMethod) -> Result<EvalFn, EvalError> {
                 )))
             }
         },
-        soption::TYPE_ID => match method.method_id() {
+        soption::TYPE_CODE => match method.method_id() {
             soption::MAP_METHOD_ID => self::soption::MAP_EVAL_FN,
             soption::FILTER_METHOD_ID => self::soption::FILTER_EVAL_FN,
             method_id => {
diff --git a/ergotree-ir/Cargo.toml b/ergotree-ir/Cargo.toml
index b2ab1d054..94124c301 100644
--- a/ergotree-ir/Cargo.toml
+++ b/ergotree-ir/Cargo.toml
@@ -33,6 +33,8 @@ bounded-vec = { version = "^0.4.0" }
 num256 = "0.3.1"
 num-traits = "0.2.14"
 num-derive = "0.3.3"
+strum = "0.21"
+strum_macros = "0.21"
 
 [features]
 default = []
diff --git a/ergotree-ir/src/serialization/method_call.rs b/ergotree-ir/src/serialization/method_call.rs
index 96435596a..a0cbd6576 100644
--- a/ergotree-ir/src/serialization/method_call.rs
+++ b/ergotree-ir/src/serialization/method_call.rs
@@ -12,7 +12,7 @@ use super::SigmaSerializeResult;
 
 impl SigmaSerializable for MethodCall {
     fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> SigmaSerializeResult {
-        self.method.obj_type.type_id().sigma_serialize(w)?;
+        self.method.obj_type.type_code().sigma_serialize(w)?;
         self.method.method_id().sigma_serialize(w)?;
         self.obj.sigma_serialize(w)?;
         self.args.sigma_serialize(w)?;
diff --git a/ergotree-ir/src/serialization/property_call.rs b/ergotree-ir/src/serialization/property_call.rs
index 82d2556bd..6873f1a94 100644
--- a/ergotree-ir/src/serialization/property_call.rs
+++ b/ergotree-ir/src/serialization/property_call.rs
@@ -12,7 +12,7 @@ use super::SigmaSerializeResult;
 
 impl SigmaSerializable for PropertyCall {
     fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> SigmaSerializeResult {
-        self.method.obj_type.type_id().sigma_serialize(w)?;
+        self.method.obj_type.type_code().sigma_serialize(w)?;
         self.method.method_id().sigma_serialize(w)?;
         self.obj.sigma_serialize(w)?;
         Ok(())
diff --git a/ergotree-ir/src/types/sbox.rs b/ergotree-ir/src/types/sbox.rs
index f9c575ace..57c02fa7a 100644
--- a/ergotree-ir/src/types/sbox.rs
+++ b/ergotree-ir/src/types/sbox.rs
@@ -7,12 +7,13 @@ use super::smethod::SMethodDesc;
 use super::stuple::STuple;
 use super::stype::SType;
 use super::stype_companion::STypeCompanion;
-use super::stype_companion::STypeCompanionHead;
 use super::stype_param::STypeVar;
 use lazy_static::lazy_static;
 
-/// SBox type id
-pub const TYPE_ID: TypeCode = TypeCode::SBOX;
+/// SBox type code
+pub const TYPE_CODE: TypeCode = TypeCode::SBOX;
+/// SBox type name
+pub static TYPE_NAME: &str = "Box";
 /// Box.value property
 pub const VALUE_METHOD_ID: MethodId = MethodId(1);
 /// Box.Rx property
@@ -20,21 +21,15 @@ pub const GET_REG_METHOD_ID: MethodId = MethodId(7);
 /// Box.tokens property
 pub const TOKENS_METHOD_ID: MethodId = MethodId(8);
 
-static S_BOX_TYPE_COMPANION_HEAD: STypeCompanionHead = STypeCompanionHead {
-    type_id: TYPE_ID,
-    type_name: "Box",
-};
-
 lazy_static! {
-    /// Box object type companion
-    pub static ref S_BOX_TYPE_COMPANION: STypeCompanion = STypeCompanion::new(
-        &S_BOX_TYPE_COMPANION_HEAD,
+    /// Box method descriptors
+    pub(crate) static ref METHOD_DESC: Vec<&'static SMethodDesc> =
         vec![
             &GET_REG_METHOD_DESC,
             &VALUE_METHOD_DESC,
             &TOKENS_METHOD_DESC
         ]
-    );
+    ;
 }
 
 lazy_static! {
@@ -48,7 +43,7 @@ lazy_static! {
         },
     };
     /// Box.value
-    pub static ref VALUE_METHOD: SMethod = SMethod::new(&S_BOX_TYPE_COMPANION, VALUE_METHOD_DESC.clone(),);
+    pub static ref VALUE_METHOD: SMethod = SMethod::new(STypeCompanion::Box, VALUE_METHOD_DESC.clone(),);
 }
 
 lazy_static! {
@@ -63,7 +58,7 @@ lazy_static! {
     };
     /// Box.getReg
     pub static ref GET_REG_METHOD: SMethod =
-        SMethod::new(&S_BOX_TYPE_COMPANION, GET_REG_METHOD_DESC.clone(),);
+        SMethod::new(STypeCompanion::Box, GET_REG_METHOD_DESC.clone(),);
 }
 
 lazy_static! {
@@ -82,7 +77,7 @@ lazy_static! {
     };
     /// Box.tokens
     pub static ref TOKENS_METHOD: SMethod =
-        SMethod::new(&S_BOX_TYPE_COMPANION, TOKENS_METHOD_DESC.clone(),);
+        SMethod::new( STypeCompanion::Box,TOKENS_METHOD_DESC.clone(),);
 }
 
 #[cfg(test)]
@@ -91,8 +86,8 @@ mod tests {
 
     #[test]
     fn test_from_ids() {
-        assert!(SMethod::from_ids(TYPE_ID, VALUE_METHOD_ID).map(|e| e.name()) == Ok("value"));
-        assert!(SMethod::from_ids(TYPE_ID, GET_REG_METHOD_ID).map(|e| e.name()) == Ok("getReg"));
-        assert!(SMethod::from_ids(TYPE_ID, TOKENS_METHOD_ID).map(|e| e.name()) == Ok("tokens"));
+        assert!(SMethod::from_ids(TYPE_CODE, VALUE_METHOD_ID).map(|e| e.name()) == Ok("value"));
+        assert!(SMethod::from_ids(TYPE_CODE, GET_REG_METHOD_ID).map(|e| e.name()) == Ok("getReg"));
+        assert!(SMethod::from_ids(TYPE_CODE, TOKENS_METHOD_ID).map(|e| e.name()) == Ok("tokens"));
     }
 }
diff --git a/ergotree-ir/src/types/scoll.rs b/ergotree-ir/src/types/scoll.rs
index f09876fa2..240a7c5a0 100644
--- a/ergotree-ir/src/types/scoll.rs
+++ b/ergotree-ir/src/types/scoll.rs
@@ -1,18 +1,19 @@
 use crate::serialization::types::TypeCode;
 use crate::types::stuple::STuple;
+use crate::types::stype_companion::STypeCompanion;
 
 use super::sfunc::SFunc;
 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_param::STypeVar;
 use lazy_static::lazy_static;
 
-/// type id
-pub const TYPE_ID: TypeCode = TypeCode::COLL;
+/// SColl type code
+pub const TYPE_CODE: TypeCode = TypeCode::COLL;
+/// SColl type name
+pub static TYPE_NAME: &str = "Coll";
 /// Coll.indexOf
 pub const INDEX_OF_METHOD_ID: MethodId = MethodId(26);
 /// Coll.flatmap
@@ -28,15 +29,9 @@ pub const UPDATED_METHOD_ID: MethodId = MethodId(20);
 /// Coll.updateMany
 pub const UPDATE_MANY_METHOD_ID: MethodId = MethodId(21);
 
-static S_COLL_TYPE_COMPANION_HEAD: STypeCompanionHead = STypeCompanionHead {
-    type_id: TYPE_ID,
-    type_name: "Coll",
-};
-
 lazy_static! {
-    /// Coll object type companion
-    pub static ref S_COLL_TYPE_COMPANION: STypeCompanion = STypeCompanion::new(
-        &S_COLL_TYPE_COMPANION_HEAD,
+    /// Coll method descriptors
+    pub(crate) static ref METHOD_DESC: Vec<&'static SMethodDesc> =
         vec![
             &INDEX_OF_METHOD_DESC,
             &FLATMAP_METHOD_DESC,
@@ -46,7 +41,7 @@ lazy_static! {
             &UPDATE_MANY_METHOD_DESC,
             &PATCH_METHOD_DESC,
         ]
-    );
+    ;
 }
 
 lazy_static! {
@@ -63,7 +58,7 @@ lazy_static! {
         },
     };
     /// Coll.indexOf
-    pub static ref INDEX_OF_METHOD: SMethod = SMethod::new(&S_COLL_TYPE_COMPANION, INDEX_OF_METHOD_DESC.clone());
+    pub static ref INDEX_OF_METHOD: SMethod = SMethod::new(STypeCompanion::Coll, INDEX_OF_METHOD_DESC.clone());
 }
 
 lazy_static! {
@@ -82,7 +77,7 @@ lazy_static! {
         ),
     };
     /// Coll.flatMap
-    pub static ref FLATMAP_METHOD: SMethod = SMethod::new(&S_COLL_TYPE_COMPANION, FLATMAP_METHOD_DESC.clone());
+    pub static ref FLATMAP_METHOD: SMethod = SMethod::new(STypeCompanion::Coll, FLATMAP_METHOD_DESC.clone());
 }
 
 lazy_static! {
@@ -100,7 +95,7 @@ lazy_static! {
         )
     };
     /// Coll.zip
-    pub static ref ZIP_METHOD: SMethod = SMethod::new(&S_COLL_TYPE_COMPANION, ZIP_METHOD_DESC.clone());
+    pub static ref ZIP_METHOD: SMethod = SMethod::new(STypeCompanion::Coll, ZIP_METHOD_DESC.clone());
 }
 
 lazy_static! {
@@ -115,7 +110,7 @@ lazy_static! {
         )
     };
     /// Coll.indices
-    pub static ref INDICES_METHOD: SMethod = SMethod::new(&S_COLL_TYPE_COMPANION, INDICES_METHOD_DESC.clone());
+    pub static ref INDICES_METHOD: SMethod = SMethod::new(STypeCompanion::Coll, INDICES_METHOD_DESC.clone());
 }
 
 lazy_static! {
@@ -133,7 +128,7 @@ lazy_static! {
         )
     };
     /// Coll.patch
-    pub static ref PATCH_METHOD: SMethod = SMethod::new(&S_COLL_TYPE_COMPANION, PATCH_METHOD_DESC.clone());
+    pub static ref PATCH_METHOD: SMethod = SMethod::new(STypeCompanion::Coll, PATCH_METHOD_DESC.clone());
 }
 
 lazy_static! {
@@ -151,7 +146,7 @@ lazy_static! {
         )
     };
     /// Coll.updated
-    pub static ref UPDATED_METHOD: SMethod = SMethod::new(&S_COLL_TYPE_COMPANION, UPDATED_METHOD_DESC.clone());
+    pub static ref UPDATED_METHOD: SMethod = SMethod::new(STypeCompanion::Coll, UPDATED_METHOD_DESC.clone());
 }
 
 lazy_static! {
@@ -169,7 +164,7 @@ lazy_static! {
         )
     };
     /// Coll.updateMany
-    pub static ref UPDATE_MANY_METHOD: SMethod = SMethod::new(&S_COLL_TYPE_COMPANION, UPDATE_MANY_METHOD_DESC.clone());
+    pub static ref UPDATE_MANY_METHOD: SMethod = SMethod::new(STypeCompanion::Coll, UPDATE_MANY_METHOD_DESC.clone());
 }
 
 #[cfg(test)]
@@ -178,14 +173,17 @@ mod tests {
 
     #[test]
     fn test_from_ids() {
-        assert!(SMethod::from_ids(TYPE_ID, INDEX_OF_METHOD_ID).map(|e| e.name()) == Ok("indexOf"));
-        assert!(SMethod::from_ids(TYPE_ID, FLATMAP_METHOD_ID).map(|e| e.name()) == Ok("flatMap"));
-        assert!(SMethod::from_ids(TYPE_ID, ZIP_METHOD_ID).map(|e| e.name()) == Ok("zip"));
-        assert!(SMethod::from_ids(TYPE_ID, INDICES_METHOD_ID).map(|e| e.name()) == Ok("indices"));
-        assert!(SMethod::from_ids(TYPE_ID, PATCH_METHOD_ID).map(|e| e.name()) == Ok("patch"));
-        assert!(SMethod::from_ids(TYPE_ID, UPDATED_METHOD_ID).map(|e| e.name()) == Ok("updated"));
         assert!(
-            SMethod::from_ids(TYPE_ID, UPDATE_MANY_METHOD_ID).map(|e| e.name()) == Ok("updateMany")
+            SMethod::from_ids(TYPE_CODE, INDEX_OF_METHOD_ID).map(|e| e.name()) == Ok("indexOf")
+        );
+        assert!(SMethod::from_ids(TYPE_CODE, FLATMAP_METHOD_ID).map(|e| e.name()) == Ok("flatMap"));
+        assert!(SMethod::from_ids(TYPE_CODE, ZIP_METHOD_ID).map(|e| e.name()) == Ok("zip"));
+        assert!(SMethod::from_ids(TYPE_CODE, INDICES_METHOD_ID).map(|e| e.name()) == Ok("indices"));
+        assert!(SMethod::from_ids(TYPE_CODE, PATCH_METHOD_ID).map(|e| e.name()) == Ok("patch"));
+        assert!(SMethod::from_ids(TYPE_CODE, UPDATED_METHOD_ID).map(|e| e.name()) == Ok("updated"));
+        assert!(
+            SMethod::from_ids(TYPE_CODE, UPDATE_MANY_METHOD_ID).map(|e| e.name())
+                == Ok("updateMany")
         );
     }
 }
diff --git a/ergotree-ir/src/types/scontext.rs b/ergotree-ir/src/types/scontext.rs
index 8491cf2c6..dae082006 100644
--- a/ergotree-ir/src/types/scontext.rs
+++ b/ergotree-ir/src/types/scontext.rs
@@ -1,39 +1,34 @@
 #![allow(missing_docs)]
 
 use crate::serialization::types::TypeCode;
+use crate::types::stype_companion::STypeCompanion;
 
 use super::smethod::MethodId;
 use super::smethod::SMethod;
 use super::smethod::SMethodDesc;
 use super::stype::SType;
 use super::stype::SType::{SAvlTree, SBox, SByte, SColl, SHeader, SInt, SPreHeader};
-use super::stype_companion::STypeCompanion;
-use super::stype_companion::STypeCompanionHead;
 use lazy_static::lazy_static;
 
-pub const TYPE_ID: TypeCode = TypeCode::SCONTEXT;
-
-static S_CONTEXT_TYPE_COMPANION_HEAD: STypeCompanionHead = STypeCompanionHead {
-    type_id: TYPE_ID,
-    type_name: "Context",
-};
-
-lazy_static! {
-    pub static ref S_CONTEXT_TYPE_COMPANION: STypeCompanion = STypeCompanion::new(
-        &S_CONTEXT_TYPE_COMPANION_HEAD,
-        vec![
-            &DATA_INPUTS_PROPERTY_METHOD_DESC,
-            &HEADERS_PROPERTY_METHOD_DESC,
-            &PRE_HEADERS_PROPERTY_METHOD_DESC,
-            &INPUTS_PROPERTY_METHOD_DESC,
-            &OUTPUTS_PROPERTY_METHOD_DESC,
-            &HEIGHT_PROPERTY_METHOD_DESC,
-            &SELF_PROPERTY_METHOD_DESC,
-            &SELF_BOX_INDEX_PROPERTY_METHOD_DESC,
-            &LAST_BLOCK_UTXO_ROOT_HASH_PROPERTY_METHOD_DESC,
-            &MINER_PUBKEY_PROPERTY_METHOD_DESC,
-        ]
-    );
+/// SContext type code
+pub const TYPE_CODE: TypeCode = TypeCode::SCONTEXT;
+/// SContext type name
+pub static TYPE_NAME: &str = "Context";
+
+lazy_static! {
+    /// Context method descriptors
+    pub(crate) static ref METHOD_DESC: Vec<&'static SMethodDesc> = vec![
+        &DATA_INPUTS_PROPERTY_METHOD_DESC,
+        &HEADERS_PROPERTY_METHOD_DESC,
+        &PRE_HEADERS_PROPERTY_METHOD_DESC,
+        &INPUTS_PROPERTY_METHOD_DESC,
+        &OUTPUTS_PROPERTY_METHOD_DESC,
+        &HEIGHT_PROPERTY_METHOD_DESC,
+        &SELF_PROPERTY_METHOD_DESC,
+        &SELF_BOX_INDEX_PROPERTY_METHOD_DESC,
+        &LAST_BLOCK_UTXO_ROOT_HASH_PROPERTY_METHOD_DESC,
+        &MINER_PUBKEY_PROPERTY_METHOD_DESC,
+    ];
 }
 
 // ---- Methods ----
@@ -48,7 +43,7 @@ lazy_static! {
 }
 lazy_static! {
     pub static ref DATA_INPUTS_PROPERTY: SMethod = SMethod::new(
-        &S_CONTEXT_TYPE_COMPANION,
+        STypeCompanion::Context,
         DATA_INPUTS_PROPERTY_METHOD_DESC.clone(),
     );
 }
@@ -60,7 +55,7 @@ lazy_static! {
 }
 lazy_static! {
     pub static ref HEADERS_PROPERTY: SMethod = SMethod::new(
-        &S_CONTEXT_TYPE_COMPANION,
+        STypeCompanion::Context,
         HEADERS_PROPERTY_METHOD_DESC.clone()
     );
 }
@@ -72,7 +67,7 @@ lazy_static! {
 }
 lazy_static! {
     pub static ref PRE_HEADERS_PROPERTY: SMethod = SMethod::new(
-        &S_CONTEXT_TYPE_COMPANION,
+        STypeCompanion::Context,
         PRE_HEADERS_PROPERTY_METHOD_DESC.clone()
     );
 }
@@ -83,10 +78,8 @@ lazy_static! {
         property("INPUTS", SColl(SBox.into()), INPUTS_PROPERTY_METHOD_ID);
 }
 lazy_static! {
-    static ref INPUTS_PROPERTY: SMethod = SMethod::new(
-        &S_CONTEXT_TYPE_COMPANION,
-        INPUTS_PROPERTY_METHOD_DESC.clone()
-    );
+    static ref INPUTS_PROPERTY: SMethod =
+        SMethod::new(STypeCompanion::Context, INPUTS_PROPERTY_METHOD_DESC.clone());
 }
 
 pub const OUTPUTS_PROPERTY_METHOD_ID: MethodId = MethodId(5);
@@ -96,7 +89,7 @@ lazy_static! {
 }
 lazy_static! {
     static ref OUTPUTS_PROPERTY: SMethod = SMethod::new(
-        &S_CONTEXT_TYPE_COMPANION,
+        STypeCompanion::Context,
         OUTPUTS_PROPERTY_METHOD_DESC.clone()
     );
 }
@@ -107,10 +100,8 @@ lazy_static! {
         property("HEIGHT", SInt, HEIGHT_PROPERTY_METHOD_ID);
 }
 lazy_static! {
-    static ref HEIGHT_PROPERTY: SMethod = SMethod::new(
-        &S_CONTEXT_TYPE_COMPANION,
-        HEIGHT_PROPERTY_METHOD_DESC.clone()
-    );
+    static ref HEIGHT_PROPERTY: SMethod =
+        SMethod::new(STypeCompanion::Context, HEIGHT_PROPERTY_METHOD_DESC.clone());
 }
 
 pub const SELF_PROPERTY_METHOD_ID: MethodId = MethodId(7);
@@ -120,7 +111,7 @@ lazy_static! {
 }
 lazy_static! {
     static ref SELF_PROPERTY: SMethod =
-        SMethod::new(&S_CONTEXT_TYPE_COMPANION, SELF_PROPERTY_METHOD_DESC.clone());
+        SMethod::new(STypeCompanion::Context, SELF_PROPERTY_METHOD_DESC.clone());
 }
 
 pub const SELF_BOX_INDEX_PROPERTY_METHOD_ID: MethodId = MethodId(8);
@@ -130,7 +121,7 @@ lazy_static! {
 }
 lazy_static! {
     static ref SELF_BOX_INDEX_PROPERTY: SMethod = SMethod::new(
-        &S_CONTEXT_TYPE_COMPANION,
+        STypeCompanion::Context,
         SELF_BOX_INDEX_PROPERTY_METHOD_DESC.clone()
     );
 }
@@ -145,7 +136,7 @@ lazy_static! {
 }
 lazy_static! {
     static ref LAST_BLOCK_UTXO_ROOT_HASH_PROPERTY: SMethod = SMethod::new(
-        &S_CONTEXT_TYPE_COMPANION,
+        STypeCompanion::Context,
         LAST_BLOCK_UTXO_ROOT_HASH_PROPERTY_METHOD_DESC.clone()
     );
 }
@@ -160,7 +151,7 @@ lazy_static! {
 }
 lazy_static! {
     static ref MINER_PUBKEY_PROPERTY: SMethod = SMethod::new(
-        &S_CONTEXT_TYPE_COMPANION,
+        STypeCompanion::Context,
         MINER_PUBKEY_PROPERTY_METHOD_DESC.clone()
     );
 }
@@ -176,7 +167,7 @@ mod tests {
     #[test]
     fn test_from_ids() {
         assert!(
-            SMethod::from_ids(TYPE_ID, DATA_INPUTS_PROPERTY_METHOD_ID).map(|e| e.name())
+            SMethod::from_ids(TYPE_CODE, DATA_INPUTS_PROPERTY_METHOD_ID).map(|e| e.name())
                 == Ok("dataInputs")
         );
     }
diff --git a/ergotree-ir/src/types/sglobal.rs b/ergotree-ir/src/types/sglobal.rs
index f3f74fa1a..2f3bfe010 100644
--- a/ergotree-ir/src/types/sglobal.rs
+++ b/ergotree-ir/src/types/sglobal.rs
@@ -4,32 +4,22 @@ use super::sfunc::SFunc;
 use super::smethod::MethodId;
 use super::smethod::SMethodDesc;
 use super::stype::SType;
-use super::stype_companion::STypeCompanion;
-use super::stype_companion::STypeCompanionHead;
 use lazy_static::lazy_static;
 
-/// SGlobal type id
-pub const TYPE_ID: TypeCode = TypeCode::SGLOBAL;
+/// SGlobal type code
+pub const TYPE_CODE: TypeCode = TypeCode::SGLOBAL;
+/// SGlobal type name
+pub static TYPE_NAME: &str = "Global";
 
 /// groupGenerator property
 pub const GROUP_GENERATOR_METHOD_ID: MethodId = MethodId(1);
 /// "xor" predefined function
 pub const XOR_METHOD_ID: MethodId = MethodId(2);
 
-static S_GLOBAL_TYPE_COMPANION_HEAD: STypeCompanionHead = STypeCompanionHead {
-    type_id: TYPE_ID,
-    type_name: "Global",
-};
-
 lazy_static! {
-    /// Box object type companion
-    pub static ref S_GLOBAL_TYPE_COMPANION: STypeCompanion = STypeCompanion::new(
-        &S_GLOBAL_TYPE_COMPANION_HEAD,
-        vec![
-            &GROUP_GENERATOR_METHOD_DESC,
-            &XOR_METHOD_DESC,
-        ]
-    );
+    /// Global method descriptors
+    pub(crate) static ref METHOD_DESC: Vec<&'static SMethodDesc> =
+        vec![&GROUP_GENERATOR_METHOD_DESC, &XOR_METHOD_DESC,];
 }
 
 lazy_static! {
diff --git a/ergotree-ir/src/types/sgroup_elem.rs b/ergotree-ir/src/types/sgroup_elem.rs
index 6c16c4e72..f6f76a007 100644
--- a/ergotree-ir/src/types/sgroup_elem.rs
+++ b/ergotree-ir/src/types/sgroup_elem.rs
@@ -1,35 +1,30 @@
 use crate::serialization::types::TypeCode;
+use crate::types::stype_companion::STypeCompanion;
 
 use super::sfunc::SFunc;
 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 lazy_static::lazy_static;
 
-/// SGroupElement type id
-pub const TYPE_ID: TypeCode = TypeCode::SGROUP_ELEMENT;
+/// SGroupElement type code
+pub const TYPE_CODE: TypeCode = TypeCode::SGROUP_ELEMENT;
+/// SGroupElement type name
+pub static TYPE_NAME: &str = "GroupElement";
 /// GroupElement.getEncoded
 pub const GET_ENCODED_METHOD_ID: MethodId = MethodId(2);
 /// GroupElement.negate
 pub const NEGATE_METHOD_ID: MethodId = MethodId(5);
 
-static S_GROUP_ELEM_TYPE_COMPANION_HEAD: STypeCompanionHead = STypeCompanionHead {
-    type_id: TYPE_ID,
-    type_name: "GroupElement",
-};
-
 lazy_static! {
-    /// GroupElement object type companion
-    pub static ref S_GROUP_ELEM_TYPE_COMPANION: STypeCompanion = STypeCompanion::new(
-        &S_GROUP_ELEM_TYPE_COMPANION_HEAD,
+    /// GroupElement method descriptors
+    pub(crate) static ref METHOD_DESC: Vec<&'static SMethodDesc> =
         vec![
             &GET_ENCODED_METHOD_DESC,
             &NEGATE_METHOD_DESC
         ]
-    );
+    ;
 }
 
 lazy_static! {
@@ -42,7 +37,7 @@ lazy_static! {
         )
     };
     /// GroupElement.geEncoded
-    pub static ref GET_ENCODED_METHOD: SMethod = SMethod::new(&S_GROUP_ELEM_TYPE_COMPANION, GET_ENCODED_METHOD_DESC.clone(),);
+    pub static ref GET_ENCODED_METHOD: SMethod = SMethod::new(STypeCompanion::GroupElem, GET_ENCODED_METHOD_DESC.clone(),);
 }
 
 lazy_static! {
@@ -55,7 +50,7 @@ lazy_static! {
         )
     };
     /// GroupElement.negate
-    pub static ref NEGATE_METHOD: SMethod = SMethod::new(&S_GROUP_ELEM_TYPE_COMPANION, NEGATE_METHOD_DESC.clone(),);
+    pub static ref NEGATE_METHOD: SMethod = SMethod::new(STypeCompanion::GroupElem, NEGATE_METHOD_DESC.clone(),);
 }
 
 #[cfg(test)]
@@ -65,8 +60,9 @@ mod tests {
     #[test]
     fn test_from_ids() {
         assert!(
-            SMethod::from_ids(TYPE_ID, GET_ENCODED_METHOD_ID).map(|e| e.name()) == Ok("getEncoded")
+            SMethod::from_ids(TYPE_CODE, GET_ENCODED_METHOD_ID).map(|e| e.name())
+                == Ok("getEncoded")
         );
-        assert!(SMethod::from_ids(TYPE_ID, NEGATE_METHOD_ID).map(|e| e.name()) == Ok("negate"));
+        assert!(SMethod::from_ids(TYPE_CODE, NEGATE_METHOD_ID).map(|e| e.name()) == Ok("negate"));
     }
 }
diff --git a/ergotree-ir/src/types/sheader.rs b/ergotree-ir/src/types/sheader.rs
index 5590e7882..b807bb09e 100644
--- a/ergotree-ir/src/types/sheader.rs
+++ b/ergotree-ir/src/types/sheader.rs
@@ -3,23 +3,17 @@ use crate::serialization::types::TypeCode;
 use super::smethod::MethodId;
 use super::smethod::SMethodDesc;
 use super::stype::SType;
-use super::stype_companion::STypeCompanion;
-use super::stype_companion::STypeCompanionHead;
 use crate::types::stype::SType::{SByte, SColl};
 use lazy_static::lazy_static;
 
-/// SBox type id
-pub const TYPE_ID: TypeCode = TypeCode::SHEADER;
-
-static S_HEADER_TYPE_COMPANION_HEAD: STypeCompanionHead = STypeCompanionHead {
-    type_id: TYPE_ID,
-    type_name: "Header",
-};
+/// SHeader type code
+pub const TYPE_CODE: TypeCode = TypeCode::SHEADER;
+/// SHeader type name
+pub static TYPE_NAME: &str = "Header";
 
 lazy_static! {
-    /// Box object type companion
-    pub static ref S_HEADER_TYPE_COMPANION: STypeCompanion = STypeCompanion::new(
-        &S_HEADER_TYPE_COMPANION_HEAD,
+    /// Header method descriptors
+    pub(crate) static ref METHOD_DESC: Vec<&'static SMethodDesc> =
         vec![
             &ID_METHOD_DESC,
             &VERSION_METHOD_DESC,
@@ -37,7 +31,7 @@ lazy_static! {
             &POW_DISTANCE_METHOD,
             &VOTES_METHOD,
         ]
-    );
+    ;
 }
 
 lazy_static! {
diff --git a/ergotree-ir/src/types/smethod.rs b/ergotree-ir/src/types/smethod.rs
index 194a58fb4..8f9aab676 100644
--- a/ergotree-ir/src/types/smethod.rs
+++ b/ergotree-ir/src/types/smethod.rs
@@ -3,6 +3,7 @@ use crate::serialization::sigma_byte_writer::SigmaByteWrite;
 use crate::serialization::types::TypeCode;
 use crate::serialization::SigmaParsingError;
 use std::collections::HashMap;
+use std::convert::TryFrom;
 
 use super::sfunc::SFunc;
 use super::stype::SType;
@@ -30,13 +31,13 @@ impl MethodId {
 #[derive(PartialEq, Eq, Debug, Clone)]
 pub struct SMethod {
     /// Object type companion
-    pub obj_type: &'static STypeCompanion,
+    pub obj_type: STypeCompanion,
     method_raw: SMethodDesc,
 }
 
 impl SMethod {
     /// Create new SMethod
-    pub(crate) const fn new(obj_type: &'static STypeCompanion, method_raw: SMethodDesc) -> SMethod {
+    pub(crate) const fn new(obj_type: STypeCompanion, method_raw: SMethodDesc) -> SMethod {
         SMethod {
             obj_type,
             method_raw,
@@ -48,7 +49,7 @@ impl SMethod {
         type_id: TypeCode,
         method_id: MethodId,
     ) -> Result<Self, SigmaParsingError> {
-        let obj_type = STypeCompanion::type_by_id(type_id)?;
+        let obj_type = STypeCompanion::try_from(type_id)?;
         match obj_type.method_by_id(&method_id) {
             Some(m) => Ok(m),
             None => Err(UnknownMethodId(method_id, type_id.value())),
@@ -117,7 +118,7 @@ impl SMethodDesc {
             },
         }
     }
-    pub(crate) fn as_method(&self, obj_type: &'static STypeCompanion) -> SMethod {
+    pub(crate) fn as_method(&self, obj_type: STypeCompanion) -> SMethod {
         SMethod {
             obj_type,
             method_raw: self.clone(),
diff --git a/ergotree-ir/src/types/soption.rs b/ergotree-ir/src/types/soption.rs
index 966a34646..1d8464c06 100644
--- a/ergotree-ir/src/types/soption.rs
+++ b/ergotree-ir/src/types/soption.rs
@@ -7,30 +7,25 @@ use super::stype::SType;
 use super::stype_companion::STypeCompanion;
 use super::stype_param::STypeVar;
 use crate::types::smethod::MethodId;
-use crate::types::stype_companion::STypeCompanionHead;
 use lazy_static::lazy_static;
 
-/// type id
-pub const TYPE_ID: TypeCode = TypeCode::OPTION;
+/// SOption type code
+pub const TYPE_CODE: TypeCode = TypeCode::OPTION;
+/// SOption type name
+pub static TYPE_NAME: &str = "Option";
 /// Option.map
 pub const MAP_METHOD_ID: MethodId = MethodId(7);
 /// Option.filter
 pub const FILTER_METHOD_ID: MethodId = MethodId(8);
 
-static S_OPTION_TYPE_COMPANION_HEAD: STypeCompanionHead = STypeCompanionHead {
-    type_id: TYPE_ID,
-    type_name: "Option",
-};
-
 lazy_static! {
-    /// Option object type companion
-    pub static ref S_OPTION_TYPE_COMPANION: STypeCompanion = STypeCompanion::new(
-        &S_OPTION_TYPE_COMPANION_HEAD,
+    /// Option method descriptors
+    pub(crate) static ref METHOD_DESC: Vec<&'static SMethodDesc> =
         vec![
             &MAP_METHOD_DESC,
             &FILTER_METHOD_DESC,
         ]
-    );
+    ;
 }
 
 lazy_static! {
@@ -49,7 +44,9 @@ lazy_static! {
         ),
     };
     /// Option.map
-    pub static ref MAP_METHOD: SMethod = SMethod::new(&S_OPTION_TYPE_COMPANION, MAP_METHOD_DESC.clone());
+    pub static ref MAP_METHOD: SMethod = SMethod::new(
+         STypeCompanion::Option,
+         MAP_METHOD_DESC.clone());
 }
 
 lazy_static! {
@@ -68,7 +65,9 @@ lazy_static! {
         ),
     };
     /// Option.map
-    pub static ref FILTER_METHOD: SMethod = SMethod::new(&S_OPTION_TYPE_COMPANION, FILTER_METHOD_DESC.clone());
+    pub static ref FILTER_METHOD: SMethod = SMethod::new(
+         STypeCompanion::Option,
+         FILTER_METHOD_DESC.clone());
 }
 
 #[cfg(test)]
@@ -77,7 +76,7 @@ mod tests {
 
     #[test]
     fn test_from_ids() {
-        assert!(SMethod::from_ids(TYPE_ID, MAP_METHOD_ID).map(|e| e.name()) == Ok("map"));
-        assert!(SMethod::from_ids(TYPE_ID, FILTER_METHOD_ID).map(|e| e.name()) == Ok("filter"));
+        assert!(SMethod::from_ids(TYPE_CODE, MAP_METHOD_ID).map(|e| e.name()) == Ok("map"));
+        assert!(SMethod::from_ids(TYPE_CODE, FILTER_METHOD_ID).map(|e| e.name()) == Ok("filter"));
     }
 }
diff --git a/ergotree-ir/src/types/spreheader.rs b/ergotree-ir/src/types/spreheader.rs
index 9cbda6a5a..cd08eb7df 100644
--- a/ergotree-ir/src/types/spreheader.rs
+++ b/ergotree-ir/src/types/spreheader.rs
@@ -3,23 +3,17 @@ use crate::serialization::types::TypeCode;
 use super::smethod::MethodId;
 use super::smethod::SMethodDesc;
 use super::stype::SType;
-use super::stype_companion::STypeCompanion;
-use super::stype_companion::STypeCompanionHead;
 use crate::types::stype::SType::{SByte, SColl};
 use lazy_static::lazy_static;
 
-/// SBox type id
-pub const TYPE_ID: TypeCode = TypeCode::SPRE_HEADER;
-
-static S_PRE_HEADER_TYPE_COMPANION_HEAD: STypeCompanionHead = STypeCompanionHead {
-    type_id: TYPE_ID,
-    type_name: "PreHeader",
-};
+/// SPreHeader type code
+pub const TYPE_CODE: TypeCode = TypeCode::SPRE_HEADER;
+/// SPreHeader type name
+pub static TYPE_NAME: &str = "PreHeader";
 
 lazy_static! {
-    /// Box object type companion
-    pub static ref S_PRE_HEADER_TYPE_COMPANION: STypeCompanion = STypeCompanion::new(
-        &S_PRE_HEADER_TYPE_COMPANION_HEAD,
+    /// Pre-header method descriptors
+    pub(crate) static ref METHOD_DESC: Vec<&'static SMethodDesc> =
         vec![
             &VERSION_METHOD,
             &PARENT_ID_METHOD,
@@ -29,7 +23,7 @@ lazy_static! {
             &MINER_PK_METHOD,
             &VOTES_METHOD,
         ]
-    );
+    ;
 }
 
 lazy_static! {
diff --git a/ergotree-ir/src/types/stype_companion.rs b/ergotree-ir/src/types/stype_companion.rs
index a08316328..bedecb3ac 100644
--- a/ergotree-ir/src/types/stype_companion.rs
+++ b/ergotree-ir/src/types/stype_companion.rs
@@ -1,3 +1,4 @@
+use std::convert::TryFrom;
 use std::fmt::Debug;
 
 use crate::serialization::types::TypeCode;
@@ -14,74 +15,100 @@ use super::smethod::SMethod;
 use super::smethod::SMethodDesc;
 use super::soption;
 use super::spreheader;
-
-#[derive(PartialEq, Eq, Debug, Clone)]
-pub(crate) struct STypeCompanionHead {
-    pub type_id: TypeCode,
-    pub type_name: &'static str,
-}
+use strum::IntoEnumIterator;
+use strum_macros::EnumIter;
 
 /// Object's type companion
-#[derive(PartialEq, Eq, Debug, Clone)]
-pub struct STypeCompanion {
-    head: &'static STypeCompanionHead,
-    methods: Vec<&'static SMethodDesc>,
+#[derive(PartialEq, Eq, Debug, Clone, Copy, EnumIter)]
+pub enum STypeCompanion {
+    /// Context
+    Context,
+    /// Box
+    Box,
+    /// Coll
+    Coll,
+    /// Group element
+    GroupElem,
+    /// Global
+    Global,
+    /// Header
+    Header,
+    /// Pre-header
+    PreHeader,
+    /// Option
+    Option,
 }
 
 impl STypeCompanion {
-    pub(crate) const fn new(
-        head: &'static STypeCompanionHead,
-        methods: Vec<&'static SMethodDesc>,
-    ) -> Self {
-        STypeCompanion { head, methods }
-    }
-
-    /// Get type companion for a givec type id
-    pub fn type_by_id(type_id: TypeCode) -> Result<&'static STypeCompanion, SigmaParsingError> {
-        Ok(if type_id == scontext::S_CONTEXT_TYPE_COMPANION.type_id() {
-            &scontext::S_CONTEXT_TYPE_COMPANION
-        } else if type_id == sbox::S_BOX_TYPE_COMPANION.type_id() {
-            &sbox::S_BOX_TYPE_COMPANION
-        } else if type_id == scoll::S_COLL_TYPE_COMPANION.type_id() {
-            &scoll::S_COLL_TYPE_COMPANION
-        } else if type_id == sgroup_elem::S_GROUP_ELEM_TYPE_COMPANION.type_id() {
-            &sgroup_elem::S_GROUP_ELEM_TYPE_COMPANION
-        } else if type_id == sglobal::S_GLOBAL_TYPE_COMPANION.type_id() {
-            &sglobal::S_GLOBAL_TYPE_COMPANION
-        } else if type_id == sheader::S_HEADER_TYPE_COMPANION.type_id() {
-            &sheader::S_HEADER_TYPE_COMPANION
-        } else if type_id == spreheader::S_PRE_HEADER_TYPE_COMPANION.type_id() {
-            &spreheader::S_PRE_HEADER_TYPE_COMPANION
-        } else if type_id == soption::S_OPTION_TYPE_COMPANION.type_id() {
-            &soption::S_OPTION_TYPE_COMPANION
-        } else {
-            return Err(SigmaParsingError::NotImplementedYet(format!(
-                "cannot find STypeCompanion for {0:?} type id",
-                type_id,
-            )));
-        })
+    fn method_desc<'a>(&'a self) -> &'a Vec<&'static SMethodDesc> {
+        match self {
+            STypeCompanion::Context => &*scontext::METHOD_DESC,
+            STypeCompanion::Box => &*sbox::METHOD_DESC,
+            STypeCompanion::Coll => &*scoll::METHOD_DESC,
+            STypeCompanion::GroupElem => &*sgroup_elem::METHOD_DESC,
+            STypeCompanion::Global => &*sglobal::METHOD_DESC,
+            STypeCompanion::Header => &*sheader::METHOD_DESC,
+            STypeCompanion::PreHeader => &*spreheader::METHOD_DESC,
+            STypeCompanion::Option => &*soption::METHOD_DESC,
+        }
     }
 
     /// Get method signature for this object by a method id
-    pub fn method_by_id(&'static self, method_id: &MethodId) -> Option<SMethod> {
-        self.methods
+    pub fn method_by_id(&self, method_id: &MethodId) -> Option<SMethod> {
+        self.method_desc()
             .iter()
             .find(|m| m.method_id == *method_id)
-            .map(|m| m.as_method(self))
+            .map(|m| m.as_method(*self))
     }
 
     /// Get list of method signatures for this object's type companion
-    pub fn methods(&'static self) -> Vec<SMethod> {
-        self.methods.iter().map(|m| m.as_method(self)).collect()
+    pub fn methods(&self) -> Vec<SMethod> {
+        self.method_desc()
+            .iter()
+            .map(|m| m.as_method(*self))
+            .collect()
     }
 
-    /// Get object type id for this type companion
-    pub fn type_id(&'static self) -> TypeCode {
-        self.head.type_id
+    /// Get object's type code
+    pub fn type_code(&self) -> TypeCode {
+        match self {
+            STypeCompanion::Context => scontext::TYPE_CODE,
+            STypeCompanion::Box => sbox::TYPE_CODE,
+            STypeCompanion::Coll => scoll::TYPE_CODE,
+            STypeCompanion::GroupElem => sgroup_elem::TYPE_CODE,
+            STypeCompanion::Global => sglobal::TYPE_CODE,
+            STypeCompanion::Header => sheader::TYPE_CODE,
+            STypeCompanion::PreHeader => spreheader::TYPE_CODE,
+            STypeCompanion::Option => soption::TYPE_CODE,
+        }
     }
 
     /// Get object's type name
-    pub fn type_name(&'static self) -> &'static str {
-        self.head.type_name
+    pub fn type_name(&self) -> &'static str {
+        match self {
+            STypeCompanion::Context => scontext::TYPE_NAME,
+            STypeCompanion::Box => sbox::TYPE_NAME,
+            STypeCompanion::Coll => scoll::TYPE_NAME,
+            STypeCompanion::GroupElem => sgroup_elem::TYPE_NAME,
+            STypeCompanion::Global => sglobal::TYPE_NAME,
+            STypeCompanion::Header => sheader::TYPE_NAME,
+            STypeCompanion::PreHeader => spreheader::TYPE_NAME,
+            STypeCompanion::Option => soption::TYPE_NAME,
+        }
+    }
+}
+
+impl TryFrom<TypeCode> for STypeCompanion {
+    type Error = SigmaParsingError;
+    fn try_from(value: TypeCode) -> Result<Self, Self::Error> {
+        for (type_code, type_companion) in STypeCompanion::iter().map(|v| (v.type_code(), v)) {
+            if type_code == value {
+                return Ok(type_companion);
+            }
+        }
+        Err(SigmaParsingError::NotImplementedYet(format!(
+            "cannot find STypeCompanion for {0:?} type id",
+            value,
+        )))
     }
 }


Test patch: