Repository: paritytech/ink
Pull request #449 (2020-06-13 21:51:44)

Avoid heap allocations for contract dispatch
This PR implements the contract message and constructor dispatch without the need for heap allocations.

Closes: https://github.com/paritytech/ink/issues/442

codecov-commenter: # [Codecov](https://codecov.io/gh/paritytech/ink/pull/449?src=pr&el=h1) Report
> Merging [#449](https://codecov.io/gh/paritytech/ink/pull/449?src=pr&el=desc) into [master](https://codecov.io/gh/paritytech/ink/commit/2021becfd15be0ca9cd0ff2f3d4d62566b65a8d5&el=desc) will **decrease** coverage by `0.36%`.
> The diff coverage is `97.33%`.

[![Impacted file tree graph](https://codecov.io/gh/paritytech/ink/pull/449/graphs/tree.svg?width=650&height=150&src=pr&token=tYyVaCZb7o)](https://codecov.io/gh/paritytech/ink/pull/449?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #449      +/-   ##
==========================================
- Coverage   86.37%   86.01%   -0.37%     
==========================================
  Files         110      109       -1     
  Lines        4249     4304      +55     
==========================================
+ Hits         3670     3702      +32     
- Misses        579      602      +23     
```


| [Impacted Files](https://codecov.io/gh/paritytech/ink/pull/449?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [core/src/env/api.rs](https://codecov.io/gh/paritytech/ink/pull/449/diff?src=pr&el=tree#diff-Y29yZS9zcmMvZW52L2FwaS5ycw==) | `73.33% <ø> (ø)` | |
| [core/src/env/engine/off\_chain/impls.rs](https://codecov.io/gh/paritytech/ink/pull/449/diff?src=pr&el=tree#diff-Y29yZS9zcmMvZW52L2VuZ2luZS9vZmZfY2hhaW4vaW1wbHMucnM=) | `80.00% <ø> (ø)` | |
| [lang/macro/src/codegen/dispatch.rs](https://codecov.io/gh/paritytech/ink/pull/449/diff?src=pr&el=tree#diff-bGFuZy9tYWNyby9zcmMvY29kZWdlbi9kaXNwYXRjaC5ycw==) | `97.85% <97.33%> (+0.26%)` | :arrow_up: |
| [primitives/src/hash.rs](https://codecov.io/gh/paritytech/ink/pull/449/diff?src=pr&el=tree#diff-cHJpbWl0aXZlcy9zcmMvaGFzaC5ycw==) | `16.66% <0.00%> (-37.50%)` | :arrow_down: |
| [primitives/src/key.rs](https://codecov.io/gh/paritytech/ink/pull/449/diff?src=pr&el=tree#diff-cHJpbWl0aXZlcy9zcmMva2V5LnJz) | `52.63% <0.00%> (-26.32%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/paritytech/ink/pull/449?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/paritytech/ink/pull/449?src=pr&el=footer). Last update [2021bec...9c53688](https://codecov.io/gh/paritytech/ink/pull/449?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

ascjones: Conflicts with master need resolving
Robbepop: > Conflicts with master need resolving

Will resolve today! :+1: 

Patch:
diff --git a/abi/Cargo.toml b/abi/Cargo.toml
index 0030eb71b4..f6fedec352 100644
--- a/abi/Cargo.toml
+++ b/abi/Cargo.toml
@@ -20,7 +20,7 @@ ink_primitives = { version = "2.1.0", path = "../primitives/", default-features
 
 serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] }
 derive_more = { version = "0.99", default-features = false, features = ["from"] }
-scale-info = { version = "0.1", default-features = false, features = ["derive"] }
+scale-info = { version = "0.2", default-features = false, features = ["derive"] }
 
 [dev-dependencies]
 serde_json = "1.0"
diff --git a/abi/src/layout2/mod.rs b/abi/src/layout2/mod.rs
index 6d27dc3f4a..fe2cdfae8b 100644
--- a/abi/src/layout2/mod.rs
+++ b/abi/src/layout2/mod.rs
@@ -25,9 +25,10 @@ use scale_info::{
         Form,
         MetaForm,
     },
+    meta_type,
     IntoCompact,
-    Metadata,
     Registry,
+    TypeInfo,
 };
 
 /// Represents the static storage layout of an ink! smart contract.
@@ -93,11 +94,11 @@ impl CellLayout {
     /// Creates a new cell layout.
     pub fn new<T>(key: LayoutKey) -> Self
     where
-        T: Metadata,
+        T: TypeInfo + 'static,
     {
         Self {
             key,
-            ty: <T as Metadata>::meta_type(),
+            ty: meta_type::<T>(),
         }
     }
 }
diff --git a/abi/src/specs.rs b/abi/src/specs.rs
index 54ca473979..5bcebbc2b3 100644
--- a/abi/src/specs.rs
+++ b/abi/src/specs.rs
@@ -28,9 +28,10 @@ use scale_info::{
         Form,
         MetaForm,
     },
+    meta_type,
     IntoCompact,
-    Metadata,
     Registry,
+    TypeInfo,
 };
 use serde::Serialize;
 
@@ -602,7 +603,7 @@ impl TypeSpec {
     /// Panics if the given display name is invalid.
     pub fn with_name_str<T>(display_name: &'static str) -> Self
     where
-        T: Metadata,
+        T: TypeInfo + 'static,
     {
         Self::with_name_segs::<T, _>(display_name.split("::"))
     }
@@ -621,11 +622,11 @@ impl TypeSpec {
     /// Panics if the given display name is invalid.
     pub fn with_name_segs<T, S>(segments: S) -> Self
     where
-        T: Metadata,
+        T: TypeInfo + 'static,
         S: IntoIterator<Item = <MetaForm as Form>::String>,
     {
         Self {
-            id: T::meta_type(),
+            id: meta_type::<T>(),
             display_name: DisplayName::from_segments(segments)
                 .expect("display name is invalid"),
         }
@@ -634,10 +635,10 @@ impl TypeSpec {
     /// Creates a new type specification without a display name.
     pub fn new<T>() -> Self
     where
-        T: Metadata,
+        T: TypeInfo + 'static,
     {
         Self {
-            id: T::meta_type(),
+            id: meta_type::<T>(),
             display_name: DisplayName::default(),
         }
     }
diff --git a/core/Cargo.toml b/core/Cargo.toml
index 1db041af41..4e76f2d391 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -39,7 +39,7 @@ blake2 = { version = "0.9", optional = true }
 # Sadly couldn't be marked as dev-dependency.
 # Never use this crate outside of the off-chain environment!
 rand = { version = "0.7", default-features = false, features = ["alloc"], optional = true }
-scale-info = { version = "0.1", default-features = false, features = ["derive"], optional = true }
+scale-info = { version = "0.2", default-features = false, features = ["derive"], optional = true }
 
 [dev-dependencies]
 itertools = "0.9"
diff --git a/core/src/env/api.rs b/core/src/env/api.rs
index aa00a738a2..3e1c46f035 100644
--- a/core/src/env/api.rs
+++ b/core/src/env/api.rs
@@ -20,7 +20,6 @@ use crate::env::{
         TypedEnv,
     },
     call::{
-        CallData,
         CallParams,
         InstantiateParams,
         ReturnType,
@@ -437,24 +436,34 @@ where
     })
 }
 
-/// Returns the input to the executed contract.
+/// Returns the execution input to the executed contract and decodes it as `T`.
 ///
 /// # Note
 ///
 /// - The input is the 4-bytes selector followed by the arguments
 ///   of the called function in their SCALE encoded representation.
-/// - This property must be received as the first action an executed
-///   contract to its environment and can only be queried once.
-///   The environment access asserts this guarantee.
+/// - No prior interaction with the environment must take place before
+///   calling this procedure.
+///
+/// # Usage
+///
+/// Normally contracts define their own `enum` dispatch types respective
+/// to their exported contructors and messages that implement `scale::Decode`
+/// according to the contructors or messages selectors and their arguments.
+/// These `enum` dispatch types are then given to this procedure as the `T`.
+///
+/// When using ink! users do not have to construct those enum dispatch types
+/// themselves as they are normally generated by the ink! code generation
+/// automatically.
 ///
 /// # Errors
 ///
-/// - If the call to `input` is not the first call to the environment.
-/// - If the input failed to decode into call data.
-///     - This happens only if the host runtime provides less than 4 bytes for
-///       the function selector upon this query.
-pub fn input() -> Result<CallData> {
-    <EnvInstance as OnInstance>::on_instance(|instance| Env::input(instance))
+/// If the given `T` cannot be properly decoded from the expected input.
+pub fn decode_input<T>() -> Result<T>
+where
+    T: scale::Decode,
+{
+    <EnvInstance as OnInstance>::on_instance(|instance| Env::decode_input::<T>(instance))
 }
 
 /// Returns the value back to the caller of the executed contract.
diff --git a/core/src/env/backend.rs b/core/src/env/backend.rs
index cb63b11b97..a9b11f290e 100644
--- a/core/src/env/backend.rs
+++ b/core/src/env/backend.rs
@@ -14,7 +14,6 @@
 
 use crate::env::{
     call::{
-        CallData,
         CallParams,
         InstantiateParams,
         ReturnType,
@@ -53,16 +52,32 @@ pub trait Env {
     where
         R: scale::Decode;
 
-    /// Returns the input to the executed contract.
+    /// Returns the execution input to the executed contract and decodes it as `T`.
     ///
     /// # Note
     ///
     /// - The input is the 4-bytes selector followed by the arguments
     ///   of the called function in their SCALE encoded representation.
-    /// - This property must be received as the first action an executed
-    ///   contract to its environment and can only be queried once.
-    ///   The environment access asserts this guarantee.
-    fn input(&mut self) -> Result<CallData>;
+    /// - No prior interaction with the environment must take place before
+    ///   calling this procedure.
+    ///
+    /// # Usage
+    ///
+    /// Normally contracts define their own `enum` dispatch types respective
+    /// to their exported contructors and messages that implement `scale::Decode`
+    /// according to the contructors or messages selectors and their arguments.
+    /// These `enum` dispatch types are then given to this procedure as the `T`.
+    ///
+    /// When using ink! users do not have to construct those enum dispatch types
+    /// themselves as they are normally generated by the ink! code generation
+    /// automatically.
+    ///
+    /// # Errors
+    ///
+    /// If the given `T` cannot be properly decoded from the expected input.
+    fn decode_input<T>(&mut self) -> Result<T>
+    where
+        T: scale::Decode;
 
     /// Returns the value back to the caller of the executed contract.
     ///
diff --git a/core/src/env/call/mod.rs b/core/src/env/call/mod.rs
index 254b44d916..76f0d02740 100644
--- a/core/src/env/call/mod.rs
+++ b/core/src/env/call/mod.rs
@@ -53,8 +53,5 @@ pub use self::{
         InstantiateBuilder,
         InstantiateParams,
     },
-    utils::{
-        CallData,
-        Selector,
-    },
+    utils::Selector,
 };
diff --git a/core/src/env/call/utils.rs b/core/src/env/call/utils.rs
index 409070bcf8..913364aee1 100644
--- a/core/src/env/call/utils.rs
+++ b/core/src/env/call/utils.rs
@@ -13,10 +13,6 @@
 // limitations under the License.
 
 use derive_more::From;
-use ink_prelude::{
-    vec,
-    vec::Vec,
-};
 
 /// Seals to guard pushing arguments to already satisfied parameter builders.
 pub mod seal {
@@ -44,82 +40,3 @@ impl Selector {
         self.bytes
     }
 }
-
-/// The raw ABI respecting input data to a call.
-///
-/// # Note
-///
-/// The first four bytes are the function selector and the rest are SCALE encoded inputs.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct CallData {
-    /// Already encoded function selector and inputs.
-    ///
-    /// # Note
-    ///
-    /// Has the invariant of always holding at least 4 bytes (the selector).
-    bytes: Vec<u8>,
-}
-
-impl CallData {
-    /// Creates new call ABI data for the given selector.
-    pub fn new(selector: Selector) -> Self {
-        let bytes = selector.to_bytes();
-        Self {
-            bytes: vec![bytes[0], bytes[1], bytes[2], bytes[3]],
-        }
-    }
-
-    /// Pushes the given argument onto the call ABI data in encoded form.
-    pub fn push_arg<A>(&mut self, arg: &A)
-    where
-        A: scale::Encode,
-    {
-        arg.encode_to(&mut self.bytes)
-    }
-
-    /// Returns the selector of `self`.
-    pub fn selector(&self) -> Selector {
-        debug_assert!(self.bytes.len() >= 4);
-        let bytes = [self.bytes[0], self.bytes[1], self.bytes[2], self.bytes[3]];
-        bytes.into()
-    }
-
-    /// Returns the underlying bytes of the encoded input parameters.
-    pub fn params(&self) -> &[u8] {
-        debug_assert!(self.bytes.len() >= 4);
-        &self.bytes[4..]
-    }
-
-    /// Returns the underlying byte representation.
-    pub fn to_bytes(&self) -> &[u8] {
-        &self.bytes
-    }
-}
-
-impl scale::Encode for CallData {
-    fn size_hint(&self) -> usize {
-        self.bytes.len()
-    }
-
-    fn encode_to<T: scale::Output>(&self, dest: &mut T) {
-        dest.write(self.bytes.as_slice());
-    }
-}
-
-impl scale::Decode for CallData {
-    fn decode<I: scale::Input>(
-        input: &mut I,
-    ) -> core::result::Result<Self, scale::Error> {
-        let remaining_len = input.remaining_len().unwrap_or(None).unwrap_or(0);
-        let mut bytes = Vec::with_capacity(remaining_len);
-        while let Ok(byte) = input.read_byte() {
-            bytes.push(byte);
-        }
-        if bytes.len() < 4 {
-            return Err(scale::Error::from(
-                "require at least 4 bytes for input data",
-            ))
-        }
-        Ok(Self { bytes })
-    }
-}
diff --git a/core/src/env/engine/off_chain/call_data.rs b/core/src/env/engine/off_chain/call_data.rs
new file mode 100644
index 0000000000..0f921c3d25
--- /dev/null
+++ b/core/src/env/engine/off_chain/call_data.rs
@@ -0,0 +1,84 @@
+use crate::env::call::Selector;
+use ink_prelude::{
+    vec,
+    vec::Vec,
+};
+
+/// The raw ABI respecting input data to a call.
+///
+/// # Note
+///
+/// The first four bytes are the function selector and the rest are SCALE encoded inputs.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct CallData {
+    /// Already encoded function selector and inputs.
+    ///
+    /// # Note
+    ///
+    /// Has the invariant of always holding at least 4 bytes (the selector).
+    bytes: Vec<u8>,
+}
+
+impl CallData {
+    /// Creates new call ABI data for the given selector.
+    pub fn new(selector: Selector) -> Self {
+        let bytes = selector.to_bytes();
+        Self {
+            bytes: vec![bytes[0], bytes[1], bytes[2], bytes[3]],
+        }
+    }
+
+    /// Pushes the given argument onto the call ABI data in encoded form.
+    pub fn push_arg<A>(&mut self, arg: &A)
+    where
+        A: scale::Encode,
+    {
+        arg.encode_to(&mut self.bytes)
+    }
+
+    /// Returns the selector of `self`.
+    pub fn selector(&self) -> Selector {
+        debug_assert!(self.bytes.len() >= 4);
+        let bytes = [self.bytes[0], self.bytes[1], self.bytes[2], self.bytes[3]];
+        bytes.into()
+    }
+
+    /// Returns the underlying bytes of the encoded input parameters.
+    pub fn params(&self) -> &[u8] {
+        debug_assert!(self.bytes.len() >= 4);
+        &self.bytes[4..]
+    }
+
+    /// Returns the underlying byte representation.
+    pub fn to_bytes(&self) -> &[u8] {
+        &self.bytes
+    }
+}
+
+impl scale::Encode for CallData {
+    fn size_hint(&self) -> usize {
+        self.bytes.len()
+    }
+
+    fn encode_to<T: scale::Output>(&self, dest: &mut T) {
+        dest.write(self.bytes.as_slice());
+    }
+}
+
+impl scale::Decode for CallData {
+    fn decode<I: scale::Input>(
+        input: &mut I,
+    ) -> core::result::Result<Self, scale::Error> {
+        let remaining_len = input.remaining_len().unwrap_or(None).unwrap_or(0);
+        let mut bytes = Vec::with_capacity(remaining_len);
+        while let Ok(byte) = input.read_byte() {
+            bytes.push(byte);
+        }
+        if bytes.len() < 4 {
+            return Err(scale::Error::from(
+                "require at least 4 bytes for input data",
+            ))
+        }
+        Ok(Self { bytes })
+    }
+}
diff --git a/core/src/env/engine/off_chain/db/exec_context.rs b/core/src/env/engine/off_chain/db/exec_context.rs
index 215560f66c..92af34b2d9 100644
--- a/core/src/env/engine/off_chain/db/exec_context.rs
+++ b/core/src/env/engine/off_chain/db/exec_context.rs
@@ -14,16 +14,14 @@
 
 use super::{
     super::{
+        CallData,
         Result,
         TypedEncoded,
     },
     OffAccountId,
     OffBalance,
 };
-use crate::env::{
-    call::CallData,
-    EnvTypes,
-};
+use crate::env::EnvTypes;
 use ink_prelude::vec::Vec;
 
 pub type Bytes = Vec<u8>;
diff --git a/core/src/env/engine/off_chain/impls.rs b/core/src/env/engine/off_chain/impls.rs
index 04ff3f8fa6..23af67d2bf 100644
--- a/core/src/env/engine/off_chain/impls.rs
+++ b/core/src/env/engine/off_chain/impls.rs
@@ -19,7 +19,6 @@ use super::{
 };
 use crate::env::{
     call::{
-        CallData,
         CallParams,
         InstantiateParams,
         ReturnType,
@@ -91,12 +90,19 @@ impl Env for EnvInstance {
         self.runtime_storage.load::<R>(runtime_key)
     }
 
-    fn input(&mut self) -> Result<CallData> {
+    fn decode_input<T>(&mut self) -> Result<T>
+    where
+        T: scale::Decode,
+    {
         self.exec_context()
             .map(|exec_ctx| &exec_ctx.call_data)
-            .map(Clone::clone)
-            .map_err(|_| scale::Error::from("could not decode input call data"))
+            .map(|call_data| scale::Encode::encode(call_data))
             .map_err(Into::into)
+            .and_then(|encoded| {
+                <T as scale::Decode>::decode(&mut &encoded[..])
+                    .map_err(|_| scale::Error::from("could not decode input call data"))
+                    .map_err(Into::into)
+            })
     }
 
     fn output<R>(&mut self, return_value: &R)
diff --git a/core/src/env/engine/off_chain/mod.rs b/core/src/env/engine/off_chain/mod.rs
index 12d8dacea3..7f586220b8 100644
--- a/core/src/env/engine/off_chain/mod.rs
+++ b/core/src/env/engine/off_chain/mod.rs
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+mod call_data;
 mod db;
 mod hashing;
 mod impls;
@@ -24,6 +25,14 @@ mod types;
 #[cfg(test)]
 mod tests;
 
+pub use self::{
+    call_data::CallData,
+    db::{
+        AccountError,
+        PastPrints,
+    },
+    typed_encoded::TypedEncodedError,
+};
 use self::{
     db::{
         Account,
@@ -47,13 +56,6 @@ use self::{
         OffTimestamp,
     },
 };
-pub use self::{
-    db::{
-        AccountError,
-        PastPrints,
-    },
-    typed_encoded::TypedEncodedError,
-};
 use super::OnInstance;
 use crate::env::EnvTypes;
 use core::cell::RefCell;
@@ -198,10 +200,7 @@ impl EnvInstance {
             T::Balance::from(20),
         );
         // Initialize the execution context for the first contract execution.
-        use crate::env::call::{
-            CallData,
-            Selector,
-        };
+        use crate::env::call::Selector;
         // The below selector bytes are incorrect but since calling doesn't work
         // yet we do not have to fix this now.
         let selector_bytes_for_call = [0x00; 4];
diff --git a/core/src/env/engine/on_chain/impls.rs b/core/src/env/engine/on_chain/impls.rs
index d2954a2b31..40c8768385 100644
--- a/core/src/env/engine/on_chain/impls.rs
+++ b/core/src/env/engine/on_chain/impls.rs
@@ -18,7 +18,6 @@ use super::{
 };
 use crate::env::{
     call::{
-        CallData,
         CallParams,
         InstantiateParams,
         ReturnType,
@@ -164,8 +163,11 @@ impl Env for EnvInstance {
         Some(self.decode_scratch_buffer().map_err(Into::into))
     }
 
-    fn input(&mut self) -> Result<CallData> {
-        self.get_property::<CallData>(|| ())
+    fn decode_input<T>(&mut self) -> Result<T>
+    where
+        T: scale::Decode,
+    {
+        self.get_property::<T>(|| ())
     }
 
     fn output<R>(&mut self, return_value: &R)
diff --git a/core/src/env/types.rs b/core/src/env/types.rs
index 6b5a630bd4..ef3ab15de4 100644
--- a/core/src/env/types.rs
+++ b/core/src/env/types.rs
@@ -46,7 +46,7 @@ use scale::{
     Encode,
 };
 #[cfg(feature = "std")]
-use scale_info::Metadata;
+use scale_info::TypeInfo;
 
 /// Types that allow for simple arithmetic operations.
 ///
@@ -178,7 +178,7 @@ where
 
 /// The fundamental types of the default configuration.
 #[derive(Debug, Clone, PartialEq, Eq)]
-#[cfg_attr(feature = "std", derive(Metadata))]
+#[cfg_attr(feature = "std", derive(TypeInfo))]
 pub enum DefaultEnvTypes {}
 
 impl EnvTypes for DefaultEnvTypes {
@@ -252,7 +252,7 @@ impl scale::Decode for Call {
     From,
     Default,
 )]
-#[cfg_attr(feature = "std", derive(Metadata))]
+#[cfg_attr(feature = "std", derive(TypeInfo))]
 pub struct AccountId([u8; 32]);
 
 impl<'a> TryFrom<&'a [u8]> for AccountId {
@@ -284,7 +284,7 @@ impl<'a> TryFrom<&'a [u8]> for AccountId {
     From,
     Default,
 )]
-#[cfg_attr(feature = "std", derive(Metadata))]
+#[cfg_attr(feature = "std", derive(TypeInfo))]
 pub struct Hash([u8; 32]);
 
 impl<'a> TryFrom<&'a [u8]> for Hash {
diff --git a/core/src/storage2/alloc/allocation.rs b/core/src/storage2/alloc/allocation.rs
index 1b8678cd62..e8a72ac30b 100644
--- a/core/src/storage2/alloc/allocation.rs
+++ b/core/src/storage2/alloc/allocation.rs
@@ -33,7 +33,7 @@ use ink_primitives::Key;
 #[derive(
     Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, scale::Encode, scale::Decode,
 )]
-#[cfg_attr(feature = "std", derive(scale_info::Metadata))]
+#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
 pub struct DynamicAllocation(pub(super) u32);
 
 impl DynamicAllocation {
diff --git a/core/src/storage2/collections/bitstash/counts.rs b/core/src/storage2/collections/bitstash/counts.rs
index 8692d9c23a..c1f9caf3e6 100644
--- a/core/src/storage2/collections/bitstash/counts.rs
+++ b/core/src/storage2/collections/bitstash/counts.rs
@@ -19,7 +19,7 @@ use core::ops::{
 
 /// Stores the number of set bits for each 256-bits block in a compact `u8`.
 #[derive(Debug, Default, PartialEq, Eq, scale::Encode, scale::Decode)]
-#[cfg_attr(feature = "std", derive(scale_info::Metadata))]
+#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
 pub struct CountFree {
     /// Set bits per 256-bit chunk.
     counts: [u8; 32],
@@ -44,7 +44,7 @@ impl IndexMut<u8> for CountFree {
 }
 
 #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, scale::Encode, scale::Decode)]
-#[cfg_attr(feature = "std", derive(scale_info::Metadata))]
+#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
 pub struct FullMask(u32);
 
 impl FullMask {
diff --git a/core/src/storage2/collections/bitvec/bits256.rs b/core/src/storage2/collections/bitvec/bits256.rs
index 4c81ca5cd8..c8e61b94c2 100644
--- a/core/src/storage2/collections/bitvec/bits256.rs
+++ b/core/src/storage2/collections/bitvec/bits256.rs
@@ -22,7 +22,7 @@ use super::{
 
 /// A chunk of 256 bits.
 #[derive(Debug, Copy, Clone, PartialEq, Eq, scale::Encode, scale::Decode)]
-#[cfg_attr(feature = "std", derive(scale_info::Metadata))]
+#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
 pub struct Bits256 {
     bits: [Bits64; 4],
 }
diff --git a/core/src/storage2/collections/hashmap/mod.rs b/core/src/storage2/collections/hashmap/mod.rs
index 094a5e64a8..aa3fbdb619 100644
--- a/core/src/storage2/collections/hashmap/mod.rs
+++ b/core/src/storage2/collections/hashmap/mod.rs
@@ -90,7 +90,7 @@ where
 ///
 /// Stores the value as well as the index to its associated key.
 #[derive(Debug, scale::Encode, scale::Decode)]
-#[cfg_attr(feature = "std", derive(scale_info::Metadata))]
+#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
 struct ValueEntry<V> {
     /// The value stored in this entry.
     value: V,
diff --git a/core/src/storage2/collections/hashmap/storage.rs b/core/src/storage2/collections/hashmap/storage.rs
index b65c23911d..d19e88c068 100644
--- a/core/src/storage2/collections/hashmap/storage.rs
+++ b/core/src/storage2/collections/hashmap/storage.rs
@@ -48,12 +48,12 @@ const _: () = {
         Layout,
         StructLayout,
     };
-    use scale_info::Metadata;
+    use scale_info::TypeInfo;
 
     impl<K, V, H> StorageLayout for StorageHashMap<K, V, H>
     where
-        K: Metadata + Ord + Clone + PackedLayout + 'static,
-        V: Metadata + PackedLayout + 'static,
+        K: TypeInfo + Ord + Clone + PackedLayout + 'static,
+        V: TypeInfo + PackedLayout + 'static,
         H: LayoutCryptoHasher + Hasher,
         Key: From<<H as Hasher>::Output>,
     {
diff --git a/core/src/storage2/collections/smallvec/storage.rs b/core/src/storage2/collections/smallvec/storage.rs
index 4dccc364cb..17b132d76b 100644
--- a/core/src/storage2/collections/smallvec/storage.rs
+++ b/core/src/storage2/collections/smallvec/storage.rs
@@ -34,11 +34,11 @@ const _: () = {
         Layout,
         StructLayout,
     };
-    use scale_info::Metadata;
+    use scale_info::TypeInfo;
 
     impl<T, N> StorageLayout for SmallVec<T, N>
     where
-        T: PackedLayout + Metadata,
+        T: PackedLayout + TypeInfo + 'static,
         N: LazyArrayLength<T>,
     {
         fn layout(key_ptr: &mut KeyPtr) -> Layout {
diff --git a/core/src/storage2/collections/stash/mod.rs b/core/src/storage2/collections/stash/mod.rs
index 4433134a00..f1f7d926b6 100644
--- a/core/src/storage2/collections/stash/mod.rs
+++ b/core/src/storage2/collections/stash/mod.rs
@@ -54,7 +54,7 @@ where
 
 /// Stores general commonly required information about the storage stash.
 #[derive(Debug, scale::Encode, scale::Decode)]
-#[cfg_attr(feature = "std", derive(scale_info::Metadata))]
+#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
 struct Header {
     /// The latest vacant index.
     ///
@@ -76,7 +76,7 @@ struct Header {
 
 /// A vacant entry with previous and next vacant indices.
 #[derive(Debug, Copy, Clone, scale::Encode, scale::Decode)]
-#[cfg_attr(feature = "std", derive(scale_info::Metadata))]
+#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
 pub struct VacantEntry {
     /// The next vacant index.
     next: Index,
@@ -89,7 +89,7 @@ pub struct VacantEntry {
 /// The vacant entries within a storage stash form a doubly linked list of
 /// vacant entries that is used to quickly re-use their vacant storage.
 #[derive(Debug, scale::Encode, scale::Decode)]
-#[cfg_attr(feature = "std", derive(scale_info::Metadata))]
+#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
 pub enum Entry<T> {
     /// A vacant entry that holds the index to the next and previous vacant entry.
     Vacant(VacantEntry),
diff --git a/core/src/storage2/collections/stash/storage.rs b/core/src/storage2/collections/stash/storage.rs
index 9413824f61..4a6340ce7d 100644
--- a/core/src/storage2/collections/stash/storage.rs
+++ b/core/src/storage2/collections/stash/storage.rs
@@ -45,7 +45,7 @@ const _: () = {
         LayoutKey,
         StructLayout,
     };
-    use scale_info::Metadata;
+    use scale_info::TypeInfo;
 
     impl StorageLayout for Header {
         fn layout(key_ptr: &mut KeyPtr) -> Layout {
@@ -57,7 +57,7 @@ const _: () = {
 
     impl<T> StorageLayout for StorageStash<T>
     where
-        T: PackedLayout + Metadata + 'static,
+        T: PackedLayout + TypeInfo + 'static,
     {
         fn layout(key_ptr: &mut KeyPtr) -> Layout {
             Layout::Struct(StructLayout::new(vec![
diff --git a/core/src/storage2/collections/vec/storage.rs b/core/src/storage2/collections/vec/storage.rs
index 2f3b2aee40..e7671ce4e0 100644
--- a/core/src/storage2/collections/vec/storage.rs
+++ b/core/src/storage2/collections/vec/storage.rs
@@ -35,11 +35,11 @@ const _: () = {
         Layout,
         StructLayout,
     };
-    use scale_info::Metadata;
+    use scale_info::TypeInfo;
 
     impl<T> StorageLayout for StorageVec<T>
     where
-        T: PackedLayout + Metadata,
+        T: PackedLayout + TypeInfo + 'static,
     {
         fn layout(key_ptr: &mut KeyPtr) -> Layout {
             Layout::Struct(StructLayout::new(vec![
diff --git a/core/src/storage2/lazy/lazy_array.rs b/core/src/storage2/lazy/lazy_array.rs
index 8187a9a2b4..58190e055a 100644
--- a/core/src/storage2/lazy/lazy_array.rs
+++ b/core/src/storage2/lazy/lazy_array.rs
@@ -96,11 +96,11 @@ const _: () = {
         Layout,
         LayoutKey,
     };
-    use scale_info::Metadata;
+    use scale_info::TypeInfo;
 
     impl<T, N> StorageLayout for LazyArray<T, N>
     where
-        T: Metadata,
+        T: TypeInfo + 'static,
         N: LazyArrayLength<T>,
     {
         fn layout(key_ptr: &mut KeyPtr) -> Layout {
diff --git a/core/src/storage2/lazy/lazy_hmap.rs b/core/src/storage2/lazy/lazy_hmap.rs
index dd87cbe5c6..97080b26b1 100644
--- a/core/src/storage2/lazy/lazy_hmap.rs
+++ b/core/src/storage2/lazy/lazy_hmap.rs
@@ -160,12 +160,12 @@ const _: () = {
         Layout,
         LayoutKey,
     };
-    use scale_info::Metadata;
+    use scale_info::TypeInfo;
 
     impl<K, V, H> StorageLayout for LazyHashMap<K, V, H>
     where
         K: Ord + scale::Encode,
-        V: Metadata,
+        V: TypeInfo + 'static,
         H: Hasher + LayoutCryptoHasher,
         Key: From<<H as Hasher>::Output>,
     {
diff --git a/core/src/storage2/lazy/lazy_imap.rs b/core/src/storage2/lazy/lazy_imap.rs
index ff98fd8d5c..4d27de7674 100644
--- a/core/src/storage2/lazy/lazy_imap.rs
+++ b/core/src/storage2/lazy/lazy_imap.rs
@@ -206,11 +206,11 @@ const _: () = {
         Layout,
         LayoutKey,
     };
-    use scale_info::Metadata;
+    use scale_info::TypeInfo;
 
     impl<T> StorageLayout for LazyIndexMap<T>
     where
-        T: Metadata,
+        T: TypeInfo + 'static,
     {
         fn layout(key_ptr: &mut KeyPtr) -> Layout {
             let capacity = u32::MAX;
diff --git a/core/src/storage2/pack.rs b/core/src/storage2/pack.rs
index 46eddf087b..5520903d30 100644
--- a/core/src/storage2/pack.rs
+++ b/core/src/storage2/pack.rs
@@ -89,11 +89,11 @@ const _: () = {
         Layout,
         LayoutKey,
     };
-    use scale_info::Metadata;
+    use scale_info::TypeInfo;
 
     impl<T> StorageLayout for Pack<T>
     where
-        T: Metadata,
+        T: TypeInfo + 'static,
     {
         fn layout(key_ptr: &mut KeyPtr) -> Layout {
             Layout::Cell(CellLayout::new::<T>(LayoutKey::from(key_ptr.advance_by(1))))
diff --git a/core/src/storage2/traits/layout/impls.rs b/core/src/storage2/traits/layout/impls.rs
index 432185952d..8971defef3 100644
--- a/core/src/storage2/traits/layout/impls.rs
+++ b/core/src/storage2/traits/layout/impls.rs
@@ -41,7 +41,7 @@ use ink_prelude::{
     vec::Vec,
 };
 use ink_primitives::Key;
-use scale_info::Metadata;
+use scale_info::TypeInfo;
 
 macro_rules! impl_storage_layout_for_primitives {
     ( $($name:ty),* $(,)? ) => {
@@ -184,7 +184,7 @@ where
 
 impl<T> StorageLayout for Vec<T>
 where
-    T: Metadata + 'static,
+    T: TypeInfo + 'static,
 {
     fn layout(key_ptr: &mut KeyPtr) -> Layout {
         Layout::Cell(CellLayout::new::<Self>(LayoutKey::from(
@@ -195,8 +195,8 @@ where
 
 impl<K, V> StorageLayout for BTreeMap<K, V>
 where
-    K: Metadata + 'static,
-    V: Metadata + 'static,
+    K: TypeInfo + 'static,
+    V: TypeInfo + 'static,
 {
     fn layout(key_ptr: &mut KeyPtr) -> Layout {
         Layout::Cell(CellLayout::new::<Self>(LayoutKey::from(
@@ -207,7 +207,7 @@ where
 
 // impl<T> StorageLayout for BinaryHeap<T>
 // where
-//     T: Metadata + 'static,
+//     T: TypeInfo + 'static,
 // {
 //     fn layout(key_ptr: &mut KeyPtr) -> Layout {
 //         Layout::Cell(CellLayout::new::<Self>(LayoutKey::from(key_ptr.advance_by(1))))
@@ -216,7 +216,7 @@ where
 
 // impl<T> StorageLayout for BTreeSet<T>
 // where
-//     T: Metadata + 'static,
+//     T: TypeInfo + 'static,
 // {
 //     fn layout(key_ptr: &mut KeyPtr) -> Layout {
 //         Layout::Cell(CellLayout::new::<Self>(LayoutKey::from(key_ptr.advance_by(1))))
@@ -225,7 +225,7 @@ where
 
 // impl<T> StorageLayout for VecDeque<T>
 // where
-//     T: Metadata + 'static,
+//     T: TypeInfo + 'static,
 // {
 //     fn layout(key_ptr: &mut KeyPtr) -> Layout {
 //         Layout::Cell(CellLayout::new::<Self>(LayoutKey::from(key_ptr.advance_by(1))))
@@ -234,7 +234,7 @@ where
 
 // impl<T> StorageLayout for LinkedList<T>
 // where
-//     T: Metadata + 'static,
+//     T: TypeInfo + 'static,
 // {
 //     fn layout(key_ptr: &mut KeyPtr) -> Layout {
 //         Layout::Cell(CellLayout::new::<Self>(LayoutKey::from(key_ptr.advance_by(1))))
diff --git a/examples/delegator/Cargo.toml b/examples/delegator/Cargo.toml
index 22987fd906..136346b2ad 100644
--- a/examples/delegator/Cargo.toml
+++ b/examples/delegator/Cargo.toml
@@ -15,7 +15,7 @@ scale = { package = "parity-scale-codec", version = "1.3", default-features = fa
 adder = { version = "2.1.0", path = "adder", default-features = false, features = ["ink-as-dependency"] }
 subber = { version = "2.1.0", path = "subber", default-features = false, features = ["ink-as-dependency"] }
 accumulator = { version = "2.1.0", path = "accumulator", default-features = false, features = ["ink-as-dependency"] }
-scale-info = { version = "0.1", default-features = false, features = ["derive"], optional = true }
+scale-info = { version = "0.2", default-features = false, features = ["derive"], optional = true }
 
 [lib]
 name = "delegator"
diff --git a/examples/delegator/accumulator/Cargo.toml b/examples/delegator/accumulator/Cargo.toml
index 6885925837..5f3ed1694a 100644
--- a/examples/delegator/accumulator/Cargo.toml
+++ b/examples/delegator/accumulator/Cargo.toml
@@ -11,7 +11,7 @@ ink_core = { version = "2.1.0", path = "../../../core", default-features = false
 ink_lang = { version = "2.1.0", path = "../../../lang", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.3", default-features = false, features = ["derive"] }
-scale-info = { version = "0.1", default-features = false, features = ["derive"], optional = true }
+scale-info = { version = "0.2", default-features = false, features = ["derive"], optional = true }
 
 [lib]
 name = "accumulator"
diff --git a/examples/delegator/adder/Cargo.toml b/examples/delegator/adder/Cargo.toml
index 1eb90bcef9..36a4b60b26 100644
--- a/examples/delegator/adder/Cargo.toml
+++ b/examples/delegator/adder/Cargo.toml
@@ -13,7 +13,7 @@ ink_lang = { version = "2.1.0", path = "../../../lang", default-features = false
 accumulator = { version = "2.1.0", path = "../accumulator", default-features = false, features = ["ink-as-dependency"] }
 
 scale = { package = "parity-scale-codec", version = "1.3", default-features = false, features = ["derive"] }
-scale-info = { version = "0.1", default-features = false, features = ["derive"], optional = true }
+scale-info = { version = "0.2", default-features = false, features = ["derive"], optional = true }
 
 [lib]
 name = "adder"
diff --git a/examples/delegator/lib.rs b/examples/delegator/lib.rs
index a30e99ebfd..1d644cfe70 100644
--- a/examples/delegator/lib.rs
+++ b/examples/delegator/lib.rs
@@ -49,7 +49,7 @@ mod delegator {
     )]
     #[cfg_attr(
         feature = "std",
-        derive(::scale_info::Metadata, ::ink_core::storage2::traits::StorageLayout)
+        derive(::scale_info::TypeInfo, ::ink_core::storage2::traits::StorageLayout)
     )]
     pub enum Which {
         Adder,
diff --git a/examples/delegator/subber/Cargo.toml b/examples/delegator/subber/Cargo.toml
index 79410880b1..aa08e3c6f1 100644
--- a/examples/delegator/subber/Cargo.toml
+++ b/examples/delegator/subber/Cargo.toml
@@ -13,7 +13,7 @@ ink_lang = { version = "2.1.0", path = "../../../lang", default-features = false
 accumulator = { version = "2.1.0", path = "../accumulator", default-features = false, features = ["ink-as-dependency"] }
 
 scale = { package = "parity-scale-codec", version = "1.3", default-features = false, features = ["derive"] }
-scale-info = { version = "0.1", default-features = false, features = ["derive"], optional = true }
+scale-info = { version = "0.2", default-features = false, features = ["derive"], optional = true }
 
 [lib]
 name = "subber"
diff --git a/examples/dns/Cargo.toml b/examples/dns/Cargo.toml
index 62e97ab474..3a90281d4e 100644
--- a/examples/dns/Cargo.toml
+++ b/examples/dns/Cargo.toml
@@ -11,7 +11,7 @@ ink_core = { version = "2.1.0", path = "../../core", default-features = false }
 ink_lang = { version = "2.1.0", path = "../../lang", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.3", default-features = false, features = ["derive"] }
-scale-info = { version = "0.1", default-features = false, features = ["derive"], optional = true }
+scale-info = { version = "0.2", default-features = false, features = ["derive"], optional = true }
 
 [lib]
 name = "dns"
diff --git a/examples/dns/lib.rs b/examples/dns/lib.rs
index ef75622bc1..58c0b58e37 100644
--- a/examples/dns/lib.rs
+++ b/examples/dns/lib.rs
@@ -86,7 +86,7 @@ mod dns {
 
     /// Errors that can occur upon calling this contract.
     #[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)]
-    #[cfg_attr(feature = "std", derive(::scale_info::Metadata))]
+    #[cfg_attr(feature = "std", derive(::scale_info::TypeInfo))]
     pub enum Error {
         /// Returned if the name already exists upon registration.
         NameAlreadyExists,
@@ -213,7 +213,7 @@ mod dns {
                 AccountId::from(DEFAULT_CALLEE_HASH),
                 DEFAULT_ENDOWMENT,
                 DEFAULT_GAS_LIMIT,
-                env::call::CallData::new(env::call::Selector::new([0x00; 4])),
+                env::test::CallData::new(env::call::Selector::new([0x00; 4])),
             )
         }
 
diff --git a/examples/erc20/Cargo.toml b/examples/erc20/Cargo.toml
index b2983c2b42..d86b05e731 100644
--- a/examples/erc20/Cargo.toml
+++ b/examples/erc20/Cargo.toml
@@ -11,7 +11,7 @@ ink_core = { version = "2.1.0", path = "../../core", default-features = false }
 ink_lang = { version = "2.1.0", path = "../../lang", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.3", default-features = false, features = ["derive"] }
-scale-info = { version = "0.1", default-features = false, features = ["derive"], optional = true }
+scale-info = { version = "0.2", default-features = false, features = ["derive"], optional = true }
 
 [lib]
 name = "erc20"
diff --git a/examples/erc20/lib.rs b/examples/erc20/lib.rs
index 9915c3e1de..41059e84ad 100644
--- a/examples/erc20/lib.rs
+++ b/examples/erc20/lib.rs
@@ -260,7 +260,7 @@ mod erc20 {
                     env::account_id::<env::DefaultEnvTypes>().unwrap_or([0x0; 32].into());
                 // Create call
                 let mut data =
-                    env::call::CallData::new(env::call::Selector::new([0x00; 4]));
+                    env::test::CallData::new(env::call::Selector::new([0x00; 4])); // balance_of
                 data.push_arg(&accounts.bob);
                 // Push the new execution context to set Bob as caller
                 assert_eq!(
@@ -306,7 +306,7 @@ mod erc20 {
                     env::account_id::<env::DefaultEnvTypes>().unwrap_or([0x0; 32].into());
                 // Create call.
                 let mut data =
-                    env::call::CallData::new(env::call::Selector::new([0x00; 4]));
+                    env::test::CallData::new(env::call::Selector::new([0x00; 4])); // balance_of
                 data.push_arg(&accounts.bob);
                 // Push the new execution context to set Bob as caller.
                 assert_eq!(
diff --git a/examples/erc721/Cargo.toml b/examples/erc721/Cargo.toml
index cf51dd7c96..0fbac0cd37 100644
--- a/examples/erc721/Cargo.toml
+++ b/examples/erc721/Cargo.toml
@@ -11,7 +11,7 @@ ink_core = { version = "2.1.0", path = "../../core", default-features = false }
 ink_lang = { version = "2.1.0", path = "../../lang", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.3", default-features = false, features = ["derive"] }
-scale-info = { version = "0.1", default-features = false, features = ["derive"], optional = true }
+scale-info = { version = "0.2", default-features = false, features = ["derive"], optional = true }
 
 [lib]
 name = "erc721"
diff --git a/examples/erc721/lib.rs b/examples/erc721/lib.rs
index 38741918be..94ad339e47 100644
--- a/examples/erc721/lib.rs
+++ b/examples/erc721/lib.rs
@@ -93,7 +93,7 @@ mod erc721 {
     }
 
     #[derive(Encode, Decode, Debug, PartialEq, Eq, Copy, Clone)]
-    #[cfg_attr(feature = "std", derive(scale_info::Metadata))]
+    #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
     pub enum Error {
         NotOwner,
         NotApproved,
@@ -525,7 +525,7 @@ mod erc721 {
                     env::account_id::<env::DefaultEnvTypes>().unwrap_or([0x0; 32].into());
                 // Create call
                 let mut data =
-                    env::call::CallData::new(env::call::Selector::new([0x00; 4]));
+                    env::test::CallData::new(env::call::Selector::new([0x00; 4])); // balance_of
                 data.push_arg(&accounts.bob);
                 // Push the new execution context to set Bob as caller
                 assert_eq!(
@@ -561,7 +561,7 @@ mod erc721 {
                     env::account_id::<env::DefaultEnvTypes>().unwrap_or([0x0; 32].into());
                 // Create call
                 let mut data =
-                    env::call::CallData::new(env::call::Selector::new([0x00; 4]));
+                    env::test::CallData::new(env::call::Selector::new([0x00; 4])); // balance_of
                 data.push_arg(&accounts.bob);
                 // Push the new execution context to set Bob as caller
                 assert_eq!(
@@ -615,7 +615,7 @@ mod erc721 {
                     env::account_id::<env::DefaultEnvTypes>().unwrap_or([0x0; 32].into());
                 // Create call
                 let mut data =
-                    env::call::CallData::new(env::call::Selector::new([0x00; 4]));
+                    env::test::CallData::new(env::call::Selector::new([0x00; 4])); // balance_of
                 data.push_arg(&accounts.bob);
                 // Push the new execution context to set Bob as caller
                 assert_eq!(
@@ -678,7 +678,7 @@ mod erc721 {
                     env::account_id::<env::DefaultEnvTypes>().unwrap_or([0x0; 32].into());
                 // Create call
                 let mut data =
-                    env::call::CallData::new(env::call::Selector::new([0x00; 4]));
+                    env::test::CallData::new(env::call::Selector::new([0x00; 4])); // balance_of
                 data.push_arg(&accounts.bob);
                 // Push the new execution context to set Eve as caller
                 assert_eq!(
diff --git a/examples/flipper/Cargo.toml b/examples/flipper/Cargo.toml
index 1fee305d6b..8ed9a4b877 100644
--- a/examples/flipper/Cargo.toml
+++ b/examples/flipper/Cargo.toml
@@ -11,7 +11,7 @@ ink_core = { version = "2.1.0", path = "../../core", default-features = false }
 ink_lang = { version = "2.1.0", path = "../../lang", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.3", default-features = false, features = ["derive"] }
-scale-info = { version = "0.1", default-features = false, features = ["derive"], optional = true }
+scale-info = { version = "0.2", default-features = false, features = ["derive"], optional = true }
 
 [lib]
 name = "flipper"
diff --git a/examples/incrementer/Cargo.toml b/examples/incrementer/Cargo.toml
index 7371132e17..58128a0bc6 100644
--- a/examples/incrementer/Cargo.toml
+++ b/examples/incrementer/Cargo.toml
@@ -11,7 +11,7 @@ ink_core = { version = "2.1.0", path = "../../core", default-features = false }
 ink_lang = { version = "2.1.0", path = "../../lang", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.3", default-features = false, features = ["derive"] }
-scale-info = { version = "0.1", default-features = false, features = ["derive"], optional = true }
+scale-info = { version = "0.2", default-features = false, features = ["derive"], optional = true }
 
 [lib]
 name = "incrementer"
diff --git a/examples/multisig_plain/Cargo.toml b/examples/multisig_plain/Cargo.toml
index 9df49f1752..68866f186d 100755
--- a/examples/multisig_plain/Cargo.toml
+++ b/examples/multisig_plain/Cargo.toml
@@ -12,7 +12,7 @@ ink_lang = { version = "2.1.0", path = "../../lang", default-features = false }
 ink_prelude = { version = "2.1.0", path = "../../prelude", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.3", default-features = false, features = ["derive"] }
-scale-info = { version = "0.1", default-features = false, features = ["derive"], optional = true }
+scale-info = { version = "0.2", default-features = false, features = ["derive"], optional = true }
 
 [lib]
 name = "multisig_plain"
diff --git a/examples/multisig_plain/lib.rs b/examples/multisig_plain/lib.rs
index abff65535f..bb1b77bccb 100755
--- a/examples/multisig_plain/lib.rs
+++ b/examples/multisig_plain/lib.rs
@@ -121,7 +121,7 @@ mod multisig_plain {
     #[derive(scale::Encode, scale::Decode, Clone, Copy, SpreadLayout, PackedLayout)]
     #[cfg_attr(
         feature = "std",
-        derive(scale_info::Metadata, ink_core::storage2::traits::StorageLayout)
+        derive(scale_info::TypeInfo, ink_core::storage2::traits::StorageLayout)
     )]
     pub enum ConfirmationStatus {
         /// The transaction is already confirmed.
@@ -139,7 +139,7 @@ mod multisig_plain {
             Debug,
             PartialEq,
             Eq,
-            scale_info::Metadata,
+            scale_info::TypeInfo,
             ink_core::storage2::traits::StorageLayout
         )
     )]
@@ -293,7 +293,7 @@ mod multisig_plain {
         /// Since this message must be send by the wallet itself it has to be build as a
         /// `Transaction` and dispatched through `submit_transaction` + `invoke_transaction`:
         /// ```no_run
-        /// use ink_core::env::{DefaultEnvTypes as Env, AccountId, call::{CallData, CallParams, Selector}};
+        /// use ink_core::env::{DefaultEnvTypes as Env, AccountId, call::{CallParams, Selector}, test::CallData};
         /// use multisig_plain::{Transaction, ConfirmationStatus};
         ///
         /// // address of an existing MultiSigPlain contract
@@ -672,7 +672,7 @@ mod multisig_plain {
 
         impl Transaction {
             fn change_requirement(requirement: u32) -> Self {
-                let mut call = call::CallData::new(call::Selector::new([0x00; 4])); // change_requirement
+                let mut call = test::CallData::new(call::Selector::new([0x00; 4])); // change_requirement
                 call.push_arg(&requirement);
                 Self {
                     callee: WALLET.into(),
@@ -690,7 +690,7 @@ mod multisig_plain {
                 WALLET.into(),
                 1000000,
                 1000000,
-                call::CallData::new(call::Selector::new([0x00; 4])),
+                test::CallData::new(call::Selector::new([0x00; 4])), // dummy
             );
         }
 
diff --git a/examples/runtime-storage/Cargo.toml b/examples/runtime-storage/Cargo.toml
index 07931b8baa..bdb901e9a4 100755
--- a/examples/runtime-storage/Cargo.toml
+++ b/examples/runtime-storage/Cargo.toml
@@ -12,7 +12,7 @@ ink_core = { version = "2.1.0", path = "../../core", default-features = false }
 ink_lang = { version = "2.1.0", path = "../../lang", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.3", default-features = false, features = ["derive"] }
-scale-info = { version = "0.1", default-features = false, features = ["derive"], optional = true }
+scale-info = { version = "0.2", default-features = false, features = ["derive"], optional = true }
 
 [lib]
 name = "runtime_storage"
diff --git a/lang/macro/Cargo.toml b/lang/macro/Cargo.toml
index 1bd4c50b0d..7f364a3a37 100644
--- a/lang/macro/Cargo.toml
+++ b/lang/macro/Cargo.toml
@@ -21,11 +21,9 @@ scale = { package = "parity-scale-codec", version = "1.3", default-features = fa
 quote = "1"
 syn = { version = "1.0", features = ["parsing", "full", "extra-traits"] }
 proc-macro2 = "1.0"
-heck = "0.3"
 itertools = { version = "0.9", default-features = false }
 either = { version = "1.5", default-features = false }
 serde = { version = "1.0", default-features = false, features = ["derive"] }
-serde_json = "1.0"
 derive_more = { version = "0.99", default-features = false, features = ["from"] }
 regex = "1.3"
 sha3 = "0.8"
@@ -37,7 +35,7 @@ ink_lang = { version = "2.1.0", path = ".." }
 
 trybuild = "1.0.24"
 pretty_assertions = "0.6.1"
-scale-info = { version = "0.1", default-features = false, features = ["derive"] }
+scale-info = { version = "0.2", default-features = false, features = ["derive"] }
 
 [lib]
 name = "ink_lang_macro"
diff --git a/lang/macro/src/codegen/cross_calling.rs b/lang/macro/src/codegen/cross_calling.rs
index 47332ede7d..83c2ab847d 100644
--- a/lang/macro/src/codegen/cross_calling.rs
+++ b/lang/macro/src/codegen/cross_calling.rs
@@ -148,7 +148,7 @@ impl CrossCalling<'_> {
             #[cfg_attr(
                 feature = "std",
                 derive(
-                    ::scale_info::Metadata,
+                    ::scale_info::TypeInfo,
                     ::ink_core::storage2::traits::StorageLayout,
                 )
             )]
diff --git a/lang/macro/src/codegen/dispatch.rs b/lang/macro/src/codegen/dispatch.rs
index c91a5d8403..62ae2f347b 100644
--- a/lang/macro/src/codegen/dispatch.rs
+++ b/lang/macro/src/codegen/dispatch.rs
@@ -21,7 +21,10 @@ use crate::{
     ir,
 };
 use derive_more::From;
-use proc_macro2::TokenStream as TokenStream2;
+use proc_macro2::{
+    Ident,
+    TokenStream as TokenStream2,
+};
 use quote::{
     quote,
     quote_spanned,
@@ -48,6 +51,8 @@ impl<'a> GenerateCodeUsing for Dispatch<'a> {
 impl GenerateCode for Dispatch<'_> {
     fn generate_code(&self) -> TokenStream2 {
         let message_trait_impls = self.generate_message_trait_impls();
+        let message_dispatch_enum = self.generate_message_dispatch_enum();
+        let constructor_dispatch_enum = self.generate_constructor_dispatch_enum();
         let message_namespaces = self.generate_message_namespaces();
         let dispatch_using_mode = self.generate_dispatch_using_mode();
         let entry_points = self.generate_entry_points();
@@ -60,6 +65,8 @@ impl GenerateCode for Dispatch<'_> {
             #[cfg(not(test))]
             #cfg
             const _: () = {
+                #message_dispatch_enum
+                #constructor_dispatch_enum
                 #message_namespaces
                 #message_trait_impls
                 #dispatch_using_mode
@@ -70,6 +77,236 @@ impl GenerateCode for Dispatch<'_> {
 }
 
 impl Dispatch<'_> {
+    fn generate_dispatch_variant_ident(
+        &self,
+        message: &ir::Function,
+        prefix: &str,
+    ) -> Ident {
+        let selector = message
+            .selector()
+            .expect("encountered a non-constructor function");
+        let selector_bytes = selector.as_bytes();
+        quote::format_ident!(
+            "__{}_0x{:02X}{:02X}{:02X}{:02X}",
+            prefix,
+            selector_bytes[0],
+            selector_bytes[1],
+            selector_bytes[2],
+            selector_bytes[3]
+        )
+    }
+
+    fn generate_dispatch_variant_decode(
+        &self,
+        message: &ir::Function,
+        prefix: &str,
+    ) -> TokenStream2 {
+        let selector_bytes = *message
+            .selector()
+            .expect("encountered a non-message function")
+            .as_bytes();
+        let s0 = selector_bytes[0];
+        let s1 = selector_bytes[1];
+        let s2 = selector_bytes[2];
+        let s3 = selector_bytes[3];
+        let variant_ident = self.generate_dispatch_variant_ident(message, prefix);
+        let variant_types = message.sig.inputs().map(|arg| &arg.ty);
+        quote! {
+            [#s0, #s1, #s2, #s3] => {
+                Ok(Self::#variant_ident(
+                    #(
+                        <#variant_types as ::scale::Decode>::decode(input)?
+                    ),*
+                ))
+            }
+        }
+    }
+
+    fn generate_dispatch_variant_arm(
+        &self,
+        message: &ir::Function,
+        prefix: &str,
+    ) -> TokenStream2 {
+        let input_types = message.sig.inputs().map(|arg| &arg.ty);
+        let variant_ident = self.generate_dispatch_variant_ident(message, prefix);
+        quote! {
+            #variant_ident(#(#input_types),*)
+        }
+    }
+
+    /// Returns an iterator yielding the functions of a contract that are messages.
+    fn contract_messages<'a>(&'a self) -> impl Iterator<Item = &'a ir::Function> + 'a {
+        self.contract
+            .functions
+            .iter()
+            .filter(|function| function.is_message())
+    }
+
+    fn generate_message_dispatch_enum(&self) -> TokenStream2 {
+        let storage_ident = &self.contract.storage.ident;
+        let message_variants = self
+            .contract_messages()
+            .map(|message| self.generate_dispatch_variant_arm(message, "Message"));
+        let decode_message = self
+            .contract_messages()
+            .map(|message| self.generate_dispatch_variant_decode(message, "Message"));
+        let execute_variants = self.contract_messages()
+            .map(|function| {
+                let ident = self.generate_dispatch_variant_ident(function, "Message");
+                let arg_idents = function
+                    .sig
+                    .inputs()
+                    .map(|arg| &arg.ident)
+                    .collect::<Vec<_>>();
+                let (mut_mod, msg_trait, exec_fn) = if function
+                    .sig
+                    .is_mut()
+                    .expect("encountered non-ink! message or constructor")
+                {
+                    (
+                        Some(quote! { mut }),
+                        quote! { MessageMut },
+                        quote! { execute_message_mut },
+                    )
+                } else {
+                    (
+                        None,
+                        quote! { MessageRef },
+                        quote! { execute_message },
+                    )
+                };
+                let arg_inputs = if arg_idents.len() == 1 {
+                    quote! { #(#arg_idents),* }
+                } else {
+                    quote! { ( #(#arg_idents),* ) }
+                };
+                let selector_id = function
+                    .selector()
+                    .expect("encountered non-ink! message or constructor")
+                    .unique_id();
+                quote! {
+                    Self::#ident(#(#arg_idents),*) => {
+                        ::ink_lang::#exec_fn::<Msg<[(); #selector_id]>, _>(move |state: &#mut_mod #storage_ident| {
+                            <Msg<[(); #selector_id]> as ::ink_lang::#msg_trait>::CALLABLE(
+                                state, #arg_inputs
+                            )
+                        })
+                    }
+                }
+            });
+        quote! {
+            const _: () = {
+                pub enum MessageDispatchEnum {
+                    #( #message_variants ),*
+                }
+
+                impl ::ink_lang::MessageDispatcher for #storage_ident {
+                    type Type = MessageDispatchEnum;
+                }
+
+                impl ::scale::Decode for MessageDispatchEnum {
+                    fn decode<I: ::scale::Input>(input: &mut I) -> ::core::result::Result<Self, ::scale::Error> {
+                        match <[u8; 4] as ::scale::Decode>::decode(input)? {
+                            #(
+                                #decode_message
+                            )*
+                            _invalid => Err(::scale::Error::from("invalid message selector"))
+                        }
+                    }
+                }
+
+                impl ::ink_lang::Execute for MessageDispatchEnum {
+                    fn execute(self) -> ::core::result::Result<(), ::ink_lang::DispatchError> {
+                        match self {
+                            #(
+                                #execute_variants
+                            )*
+                        }
+                    }
+                }
+            };
+        }
+    }
+
+    /// Returns an iterator yielding the functions of a contract that are constructors.
+    fn contract_constructors<'a>(
+        &'a self,
+    ) -> impl Iterator<Item = &'a ir::Function> + 'a {
+        self.contract
+            .functions
+            .iter()
+            .filter(|function| function.is_constructor())
+    }
+
+    fn generate_constructor_dispatch_enum(&self) -> TokenStream2 {
+        let storage_ident = &self.contract.storage.ident;
+        let message_variants = self
+            .contract_constructors()
+            .map(|message| self.generate_dispatch_variant_arm(message, "Constructor"));
+        let decode_message = self
+            .contract_constructors()
+            .map(|message| self.generate_dispatch_variant_decode(message, "Constructor"));
+        let execute_variants = self.contract_constructors()
+                .map(|function| {
+                    let ident = self.generate_dispatch_variant_ident(function, "Constructor");
+                    let arg_idents = function
+                        .sig
+                        .inputs()
+                        .map(|arg| &arg.ident)
+                        .collect::<Vec<_>>();
+                    let arg_inputs = if arg_idents.len() == 1 {
+                        quote! { #(#arg_idents),* }
+                    } else {
+                        quote! { ( #(#arg_idents),* ) }
+                    };
+                    let selector_id = function
+                        .selector()
+                        .expect("encountered non-ink! message or constructor")
+                        .unique_id();
+                    quote! {
+                        Self::#ident(#(#arg_idents),*) => {
+                            ::ink_lang::execute_constructor::<Constr<[(); #selector_id]>, _>(move || {
+                                <Constr<[(); #selector_id]> as ::ink_lang::Constructor>::CALLABLE(
+                                    #arg_inputs
+                                )
+                            })
+                        }
+                    }
+                });
+        quote! {
+            const _: () = {
+                pub enum ConstructorDispatchEnum {
+                    #( #message_variants ),*
+                }
+
+                impl ::ink_lang::ConstructorDispatcher for #storage_ident {
+                    type Type = ConstructorDispatchEnum;
+                }
+
+                impl ::scale::Decode for ConstructorDispatchEnum {
+                    fn decode<I: ::scale::Input>(input: &mut I) -> ::core::result::Result<Self, ::scale::Error> {
+                        match <[u8; 4] as ::scale::Decode>::decode(input)? {
+                            #(
+                                #decode_message
+                            )*
+                            _invalid => Err(::scale::Error::from("invalid constructor selector"))
+                        }
+                    }
+                }
+
+                impl ::ink_lang::Execute for ConstructorDispatchEnum {
+                    fn execute(self) -> ::core::result::Result<(), ::ink_lang::DispatchError> {
+                        match self {
+                            #(
+                                #execute_variants
+                            )*
+                        }
+                    }
+                }
+            };
+        }
+    }
+
     fn generate_trait_impls_for_message(&self, function: &ir::Function) -> TokenStream2 {
         if !(function.is_constructor() || function.is_message()) {
             return quote! {}
@@ -219,59 +456,27 @@ impl Dispatch<'_> {
         }
     }
 
-    fn generate_dispatch_using_mode_fragment(
-        &self,
-        function: &ir::Function,
-    ) -> TokenStream2 {
-        if !(function.is_constructor() || function.is_message()) {
-            return quote! {}
-        }
-        let selector = function
-            .selector()
-            .expect("this is either a message or constructor at this point; qed");
-        let selector_id = selector.unique_id();
-        let sig = &function.sig;
-        let builder_name = if function.is_constructor() {
-            quote! { register_constructor }
-        } else if sig.is_mut().expect("must be a message if not constructor") {
-            quote! { register_message_mut }
-        } else {
-            quote! { register_message }
-        };
-        let namespace = match function.kind() {
-            ir::FunctionKind::Constructor(_) => quote! { Constr },
-            ir::FunctionKind::Message(_) => quote! { Msg },
-            ir::FunctionKind::Method => panic!("ICE: can't match a method at this point"),
-        };
-        quote! {
-            .#builder_name::<#namespace<[(); #selector_id]>>()
-        }
-    }
-
     fn generate_dispatch_using_mode(&self) -> TokenStream2 {
         let storage_ident = &self.contract.storage.ident;
-        let fragments = self
-            .contract
-            .functions
-            .iter()
-            .map(|fun| self.generate_dispatch_using_mode_fragment(fun));
-
         quote! {
             impl ::ink_lang::DispatchUsingMode for #storage_ident {
                 #[allow(unused_parens)]
                 fn dispatch_using_mode(
                     mode: ::ink_lang::DispatchMode
                 ) -> core::result::Result<(), ::ink_lang::DispatchError> {
-                    let call_data =
-                        ::ink_core::env::input().map_err(|_| ::ink_lang::DispatchError::CouldNotReadInput)?;
-                    let contract = ::ink_lang::Contract::build()
-                        #(
-                            #fragments
-                        )*
-                        .finalize();
                     match mode {
-                        ::ink_lang::DispatchMode::Instantiate => contract.on_instantiate(&call_data),
-                        ::ink_lang::DispatchMode::Call => contract.on_call(&call_data),
+                        ::ink_lang::DispatchMode::Instantiate => {
+                            <<#storage_ident as ::ink_lang::ConstructorDispatcher>::Type as ::ink_lang::Execute>::execute(
+                                ::ink_core::env::decode_input::<<#storage_ident as ::ink_lang::ConstructorDispatcher>::Type>()
+                                    .map_err(|_| ::ink_lang::DispatchError::CouldNotReadInput)?
+                            )
+                        }
+                        ::ink_lang::DispatchMode::Call => {
+                            <<#storage_ident as ::ink_lang::MessageDispatcher>::Type as ::ink_lang::Execute>::execute(
+                                ::ink_core::env::decode_input::<<#storage_ident as ::ink_lang::MessageDispatcher>::Type>()
+                                    .map_err(|_| ::ink_lang::DispatchError::CouldNotReadInput)?
+                            )
+                        }
                     }
                 }
             }
diff --git a/lang/src/contract.rs b/lang/src/contract.rs
index ffe4c92892..f7570a59d5 100644
--- a/lang/src/contract.rs
+++ b/lang/src/contract.rs
@@ -12,24 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::{
-    dispatcher::{
-        Dispatch,
-        DispatchList,
-        EmptyDispatchList,
-        MsgCon,
-        MsgMut,
-        MsgRef,
-    },
-    traits::{
-        Constructor,
-        MessageMut,
-        MessageRef,
-    },
-    DispatchError,
-};
-use core::marker::PhantomData;
-use ink_core::env::call::CallData;
+use crate::DispatchError;
 
 /// The contract dispatch mode.
 ///
@@ -48,162 +31,3 @@ pub enum DispatchMode {
 pub trait DispatchUsingMode {
     fn dispatch_using_mode(mode: DispatchMode) -> Result<(), DispatchError>;
 }
-
-/// Placeholder for the given type.
-#[derive(Debug)]
-pub struct Placeholder<T>(PhantomData<fn() -> T>);
-
-impl<T> Default for Placeholder<T> {
-    fn default() -> Self {
-        Self(Default::default())
-    }
-}
-
-impl<T> Clone for Placeholder<T> {
-    fn clone(&self) -> Self {
-        Default::default()
-    }
-}
-
-impl<T> Copy for Placeholder<T> {}
-
-/// The contract definition.
-///
-/// ## Dispatch
-///
-/// ### Constructor Dispatch
-///
-/// 1. Retrieve function selector `s`.
-/// 1. Find selected constructor `C` given `s`:
-///     - If `C` was found:
-///         1. Decode input arguments `i` according to `C`'s requirements
-///         1. Initialize dynamic storage allocator for instantiation
-///         1. Call `C(i)`, returns storage instance `S`
-///         1. Push state of `S` to contract storage
-///         1. Finalize dynamic storage allocator
-///         1. Exit
-///     - Otherwise:
-///         1. Panic that the provided constructor selector was invalid
-///
-/// ### Message Dispatch
-///
-/// 1. Retrieve function selector `s`.
-/// 1. Find selected message `M` given `s`:
-///     - If `M` was found:
-///         1. Decode input arguments `i` according to `M`'s requirements
-///         1. Initialize dynamic storage allocator for calls
-///         1. Pull storage instance `S` from contract storage
-///         1. Call `M(s, i)`, returns return value `R`
-///         1. If `M` has mutable access to the contract storage:
-///             1. push mutated state of `S` back to contract storage
-///         1. Finalize dynamic storage allocator
-///         1. If `R` is not of type `()`:
-///             1. return `R`
-///         1. Exit
-///     - Otherwise:
-///         1. Panic that the provided message selector was invalid
-#[derive(Debug)]
-pub struct Contract<Constrs, Msgs, Phase> {
-    /// The current type state of the contract.
-    state: Placeholder<(Constrs, Msgs, Phase)>,
-}
-
-impl<Constrs, Msgs, Phase> Default for Contract<Constrs, Msgs, Phase> {
-    #[inline(always)]
-    fn default() -> Self {
-        Self {
-            state: Default::default(),
-        }
-    }
-}
-
-impl<Constrs, Msgs, Phase> Clone for Contract<Constrs, Msgs, Phase> {
-    fn clone(&self) -> Self {
-        Default::default()
-    }
-}
-
-impl<Constrs, Msgs, Phase> Copy for Contract<Constrs, Msgs, Phase> {}
-
-/// Phase to start the building process of a contract.
-#[derive(Debug)]
-pub enum EmptyPhase {}
-/// Building phase of a contract construction.
-#[derive(Debug)]
-pub enum BuildPhase {}
-/// Final phase of a fully constructed contract.
-#[derive(Debug)]
-pub enum FinalPhase {}
-
-impl Contract<(), (), EmptyPhase> {
-    /// Creates a new contract builder.
-    #[inline(always)]
-    pub fn build() -> Contract<EmptyDispatchList, EmptyDispatchList, BuildPhase> {
-        Default::default()
-    }
-}
-
-impl<Constrs> Contract<Constrs, EmptyDispatchList, BuildPhase> {
-    /// Registers a new constructor for the contract.
-    #[inline(always)]
-    pub fn register_constructor<C>(
-        self,
-    ) -> Contract<DispatchList<MsgCon<C>, Constrs>, EmptyDispatchList, BuildPhase>
-    where
-        C: Constructor,
-    {
-        Default::default()
-    }
-}
-
-impl<Constrs, Messages> Contract<Constrs, Messages, BuildPhase> {
-    /// Registers a new `&self` message for the contract.
-    #[inline(always)]
-    pub fn register_message<M>(
-        self,
-    ) -> Contract<Constrs, DispatchList<MsgRef<M>, Messages>, BuildPhase>
-    where
-        M: MessageRef,
-    {
-        Default::default()
-    }
-
-    /// Registers a new `&mut self` message for the contract.
-    #[inline(always)]
-    pub fn register_message_mut<M>(
-        self,
-    ) -> Contract<Constrs, DispatchList<MsgMut<M>, Messages>, BuildPhase>
-    where
-        M: MessageMut,
-    {
-        Default::default()
-    }
-
-    /// Finalizes the construction of the contract.
-    #[inline(always)]
-    pub fn finalize(self) -> Contract<Constrs, Messages, FinalPhase> {
-        Default::default()
-    }
-}
-
-impl<Constrs, Messages> Contract<Constrs, Messages, FinalPhase>
-where
-    Constrs: Dispatch,
-{
-    /// Instantiates the contract.
-    #[inline(always)]
-    pub fn on_instantiate(self, call_data: &CallData) -> Result<(), DispatchError> {
-        <Constrs as Dispatch>::dispatch(call_data)
-    }
-}
-
-impl<Constrs, Messages> Contract<Constrs, Messages, FinalPhase>
-where
-    Messages: Dispatch,
-{
-    /// Calls a contract message.
-    #[inline(always)]
-    pub fn on_call(self, call_data: &CallData) -> Result<(), DispatchError> {
-        <Messages as Dispatch>::dispatch(call_data)
-    }
-}
diff --git a/lang/src/dispatcher.rs b/lang/src/dispatcher.rs
index 715d87b4ba..07a0c9dfdc 100644
--- a/lang/src/dispatcher.rs
+++ b/lang/src/dispatcher.rs
@@ -15,30 +15,21 @@
 use crate::{
     Constructor,
     DispatchError,
-    FnInput,
     FnOutput,
-    FnSelector,
     FnState,
     MessageMut,
     MessageRef,
-    Placeholder,
 };
 use core::{
     any::TypeId,
     mem::ManuallyDrop,
 };
-use ink_core::{
-    env::call::{
-        CallData,
-        Selector,
-    },
-    storage2::{
-        alloc,
-        alloc::ContractPhase,
-        traits::{
-            pull_spread_root,
-            push_spread_root,
-        },
+use ink_core::storage2::{
+    alloc,
+    alloc::ContractPhase,
+    traits::{
+        pull_spread_root,
+        push_spread_root,
     },
 };
 use ink_primitives::Key;
@@ -46,198 +37,89 @@ use ink_primitives::Key;
 /// Results of message handling operations.
 pub type Result<T> = core::result::Result<T, DispatchError>;
 
-/// Types implementing this trait can handle contract calls.
-pub trait Dispatch {
-    /// Dispatches the call and returns the call result.
-    fn dispatch(input: &CallData) -> Result<()>;
+/// Connector trait: Connects enum dispatcher for messages with the contract.
+pub trait MessageDispatcher {
+    /// The contract's message dispatcher type.
+    type Type;
 }
 
-/// A dispatcher that shall never dispatch.
-///
-/// # Note
-///
-/// This always comes last in a chain of dispatchers
-/// and is used to break out of the dispatch routine.
-#[derive(Copy, Clone, Default)]
-pub struct UnreachableDispatcher;
-
-impl Dispatch for UnreachableDispatcher {
-    #[inline(always)]
-    fn dispatch(_data: &CallData) -> Result<()> {
-        Err(DispatchError::UnknownSelector)
-    }
-}
-
-/// Types able to push another dispatcher to themselves.
-pub trait PushDispatcher: Sized {
-    fn push<D>(self, dispatcher: D) -> DispatchList<D, Self>;
-}
-
-impl PushDispatcher for UnreachableDispatcher {
-    /// Creates a dispatch list with `dispatcher` and `self` as elements.
-    #[inline(always)]
-    fn push<D>(self, _dispatcher: D) -> DispatchList<D, UnreachableDispatcher> {
-        Default::default()
-    }
-}
-
-/// A list of dispatchers.
-#[derive(Debug)]
-pub struct DispatchList<Head, Rest> {
-    /// Placeholder for the current dispatcher head and rest.
-    placeholder: Placeholder<(Head, Rest)>,
-}
-
-impl<Head, Rest> Default for DispatchList<Head, Rest> {
-    #[inline(always)]
-    fn default() -> Self {
-        Self {
-            placeholder: Default::default(),
-        }
-    }
-}
-
-/// A simple type definition to view the single unreachable dispatcher as list.
-pub type EmptyDispatchList = UnreachableDispatcher;
-
-impl EmptyDispatchList {
-    /// Creates a new dispatch list.
-    #[inline(always)]
-    pub fn empty() -> EmptyDispatchList {
-        UnreachableDispatcher
-    }
-}
-
-impl<Head, Rest> PushDispatcher for DispatchList<Head, Rest> {
-    /// Pushes another dispatcher onto the list.
-    #[inline(always)]
-    fn push<D>(self, _dispatcher: D) -> DispatchList<D, Self> {
-        Default::default()
-    }
-}
-
-impl<Head, Rest> Dispatch for DispatchList<Head, Rest>
-where
-    Head: Dispatch + FnSelector,
-    Rest: Dispatch,
-{
-    #[inline(always)]
-    fn dispatch(data: &CallData) -> Result<()> {
-        if <Head as FnSelector>::SELECTOR == data.selector() {
-            <Head as Dispatch>::dispatch(data)
-        } else {
-            <Rest as Dispatch>::dispatch(data)
-        }
-    }
-}
-
-/// A `&self` contract message wrapper.
-#[derive(Debug, Default)]
-pub struct MsgRef<M>
-where
-    M: MessageRef,
-{
-    message: Placeholder<M>,
+/// Connector trait: Connects enum dispatcher for constructors with the contract.
+pub trait ConstructorDispatcher {
+    /// The contract's constructors dispatcher type.
+    type Type;
 }
 
-impl<M> FnSelector for MsgRef<M>
-where
-    M: MessageRef,
-{
-    const SELECTOR: Selector = <M as FnSelector>::SELECTOR;
+/// Connector trait used to start the execution of a smart contract.
+///
+/// The generated message and constructor dispatch enums implement this trait
+/// in order to forward their already decoded state to the selected messages
+/// or constructors.
+pub trait Execute {
+    /// Starts the smart contract execution.
+    fn execute(self) -> Result<()>;
 }
 
-impl<M> Dispatch for MsgRef<M>
+/// Executes the given `&self` message closure.
+///
+/// # Note
+///
+/// The closure is supposed to already contain all the arguments that the real
+/// message requires and forwards them.
+pub fn execute_message<M, F>(f: F) -> Result<()>
 where
     M: MessageRef,
+    F: FnOnce(&<M as FnState>::State) -> <M as FnOutput>::Output,
 {
-    #[inline(always)]
-    fn dispatch(data: &CallData) -> Result<()> {
-        use scale::Decode as _;
-        let args = <M as FnInput>::Input::decode(&mut &data.params()[..])
-            .map_err(|_| DispatchError::InvalidParameters)?;
-        alloc::initialize(ContractPhase::Call);
-        let root_key = Key::from([0x00; 32]);
-        let state =
-            ManuallyDrop::new(pull_spread_root::<<M as FnState>::State>(&root_key));
-        let result = <M as MessageRef>::CALLABLE(&state, args);
-        alloc::finalize();
-        if TypeId::of::<<M as FnOutput>::Output>() != TypeId::of::<()>() {
-            ink_core::env::output::<<M as FnOutput>::Output>(&result)
-        }
-        Ok(())
+    alloc::initialize(ContractPhase::Call);
+    let root_key = Key::from([0x00; 32]);
+    let state = ManuallyDrop::new(pull_spread_root::<<M as FnState>::State>(&root_key));
+    let result = f(&state);
+    alloc::finalize();
+    if TypeId::of::<<M as FnOutput>::Output>() != TypeId::of::<()>() {
+        ink_core::env::output::<<M as FnOutput>::Output>(&result)
     }
+    Ok(())
 }
 
-/// A `&mut self` contract message wrapper.
-#[derive(Debug, Default)]
-pub struct MsgMut<M>
-where
-    M: MessageMut,
-{
-    message: Placeholder<M>,
-}
-
-impl<M> FnSelector for MsgMut<M>
-where
-    M: MessageMut,
-{
-    const SELECTOR: Selector = <M as FnSelector>::SELECTOR;
-}
-
-impl<M> Dispatch for MsgMut<M>
+/// Executes the given `&mut self` message closure.
+///
+/// # Note
+///
+/// The closure is supposed to already contain all the arguments that the real
+/// message requires and forwards them.
+pub fn execute_message_mut<M, F>(f: F) -> Result<()>
 where
     M: MessageMut,
+    F: FnOnce(&mut <M as FnState>::State) -> <M as FnOutput>::Output,
 {
-    #[inline(always)]
-    fn dispatch(data: &CallData) -> Result<()> {
-        use scale::Decode as _;
-        let args = <M as FnInput>::Input::decode(&mut &data.params()[..])
-            .map_err(|_| DispatchError::InvalidParameters)?;
-        alloc::initialize(ContractPhase::Call);
-        let root_key = Key::from([0x00; 32]);
-        let mut state =
-            ManuallyDrop::new(pull_spread_root::<<M as FnState>::State>(&root_key));
-        let result = <M as MessageMut>::CALLABLE(&mut state, args);
-        push_spread_root::<<M as FnState>::State>(&state, &root_key);
-        alloc::finalize();
-        if TypeId::of::<<M as FnOutput>::Output>() != TypeId::of::<()>() {
-            ink_core::env::output::<<M as FnOutput>::Output>(&result)
-        }
-        Ok(())
+    alloc::initialize(ContractPhase::Call);
+    let root_key = Key::from([0x00; 32]);
+    let mut state =
+        ManuallyDrop::new(pull_spread_root::<<M as FnState>::State>(&root_key));
+    let result = f(&mut state);
+    push_spread_root::<<M as FnState>::State>(&state, &root_key);
+    alloc::finalize();
+    if TypeId::of::<<M as FnOutput>::Output>() != TypeId::of::<()>() {
+        ink_core::env::output::<<M as FnOutput>::Output>(&result)
     }
+    Ok(())
 }
 
-/// A constructor contract message wrapper.
-#[derive(Debug, Default)]
-pub struct MsgCon<M>
-where
-    M: Constructor,
-{
-    message: Placeholder<M>,
-}
-
-impl<M> FnSelector for MsgCon<M>
-where
-    M: Constructor,
-{
-    const SELECTOR: Selector = <M as FnSelector>::SELECTOR;
-}
-
-impl<M> Dispatch for MsgCon<M>
+/// Executes the given constructor closure.
+///
+/// # Note
+///
+/// The closure is supposed to already contain all the arguments that the real
+/// constructor message requires and forwards them.
+pub fn execute_constructor<C, F>(f: F) -> Result<()>
 where
-    M: Constructor,
+    C: Constructor,
+    F: FnOnce() -> <C as FnState>::State,
 {
-    #[inline(always)]
-    fn dispatch(data: &CallData) -> Result<()> {
-        use scale::Decode as _;
-        let args = <M as FnInput>::Input::decode(&mut &data.params()[..])
-            .map_err(|_| DispatchError::InvalidParameters)?;
-        alloc::initialize(ContractPhase::Deploy);
-        let root_key = Key::from([0x00; 32]);
-        let state = ManuallyDrop::new(<M as Constructor>::CALLABLE(args));
-        push_spread_root::<<M as FnState>::State>(&state, &root_key);
-        alloc::finalize();
-        Ok(())
-    }
+    alloc::initialize(ContractPhase::Deploy);
+    let state = ManuallyDrop::new(f());
+    let root_key = Key::from([0x00; 32]);
+    push_spread_root::<<C as FnState>::State>(&state, &root_key);
+    alloc::finalize();
+    Ok(())
 }
diff --git a/lang/src/lib.rs b/lang/src/lib.rs
index 0343c93b80..3cb0986ed9 100644
--- a/lang/src/lib.rs
+++ b/lang/src/lib.rs
@@ -23,7 +23,6 @@ mod dispatcher;
 mod env_access;
 mod error;
 mod events;
-mod testable;
 mod traits;
 
 pub use ink_lang_macro::contract;
@@ -33,13 +32,8 @@ pub use self::abi::GenerateAbi;
 
 pub use self::{
     contract::{
-        BuildPhase,
-        Contract,
         DispatchMode,
         DispatchUsingMode,
-        EmptyPhase,
-        FinalPhase,
-        Placeholder,
     },
     cross_calling::{
         ForwardCall,
@@ -47,14 +41,12 @@ pub use self::{
         ToAccountId,
     },
     dispatcher::{
-        Dispatch,
-        DispatchList,
-        EmptyDispatchList,
-        MsgCon,
-        MsgMut,
-        MsgRef,
-        PushDispatcher,
-        UnreachableDispatcher,
+        execute_constructor,
+        execute_message,
+        execute_message_mut,
+        ConstructorDispatcher,
+        Execute,
+        MessageDispatcher,
     },
     env_access::{
         Env,
@@ -70,7 +62,6 @@ pub use self::{
         BaseEvent,
         EmitEvent,
     },
-    testable::InstantiateTestable,
     traits::{
         Constructor,
         FnInput,
diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml
index c1409d17ab..f8c1d2d550 100644
--- a/primitives/Cargo.toml
+++ b/primitives/Cargo.toml
@@ -21,7 +21,7 @@ include = ["/Cargo.toml", "src/**/*.rs", "/README.md", "/LICENSE"]
 ink_prelude = { version = "2.1.0", path = "../prelude/", default-features = false }
 tiny-keccak = { version = "2.0", features = ["keccak"] }
 scale = { package = "parity-scale-codec", version = "1.3", default-features = false, features = ["derive", "full"] }
-scale-info = { version = "0.1", default-features = false, features = ["derive"], optional = true }
+scale-info = { version = "0.2", default-features = false, features = ["derive"], optional = true }
 
 [dev-dependencies]
 criterion = "0.3.1"


Test patch:
diff --git a/core/src/env/engine/off_chain/test_api.rs b/core/src/env/engine/off_chain/test_api.rs
index ce41c81cda..a8427251e4 100644
--- a/core/src/env/engine/off_chain/test_api.rs
+++ b/core/src/env/engine/off_chain/test_api.rs
@@ -14,6 +14,7 @@
 
 //! Operations on the off-chain testing environment.
 
+pub use super::CallData;
 use super::{
     db::ExecContext,
     AccountError,
@@ -22,7 +23,6 @@ use super::{
     OnInstance,
 };
 use crate::env::{
-    call::CallData,
     EnvTypes,
     Result,
 };
diff --git a/lang/macro/tests/ui/pass/05-erc721-contract.rs b/lang/macro/tests/ui/pass/05-erc721-contract.rs
index beadd6011f..5c8e8b8c9c 100644
--- a/lang/macro/tests/ui/pass/05-erc721-contract.rs
+++ b/lang/macro/tests/ui/pass/05-erc721-contract.rs
@@ -8,7 +8,7 @@ mod erc721 {
     pub type TokenId = u32;
 
     #[derive(scale::Encode, scale::Decode)]
-    #[cfg_attr(feature = "std", derive(scale_info::Metadata))]
+    #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
     pub enum Error {
         SpecifiedTokenHasNoOwner,
         ApprovalToCurrentOwner,
diff --git a/lang/src/testable.rs b/lang/src/testable.rs
deleted file mode 100644
index b94a13e52c..0000000000
--- a/lang/src/testable.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2018-2019 Parity Technologies (UK) Ltd.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-/// Trait implemented by contracts to make them testable.
-///
-/// The testability comes from converting `#[ink(constructor)]`
-/// functions from `&mut self` methods into making them actual
-/// Rust constructors: e.g. with signatures like `fn new() -> Self`.
-pub trait InstantiateTestable: Sized {
-    /// The test wrapper for the contract.
-    type Wrapped: core::ops::Deref<Target = Self> + core::ops::DerefMut<Target = Self>;
-
-    /// Creates a testable instantiation of the contract.
-    fn instantiate() -> Self::Wrapped;
-}