diff --git a/aptos-move/framework/aptos-stdlib/aptos-framework/releases/artifacts/current/error_description/error_description.errmap b/aptos-move/framework/aptos-stdlib/aptos-framework/releases/artifacts/current/error_description/error_description.errmap
new file mode 100644
index 0000000000000..4dd6c3b82bf32
Binary files /dev/null and b/aptos-move/framework/aptos-stdlib/aptos-framework/releases/artifacts/current/error_description/error_description.errmap differ
diff --git a/aptos-move/framework/aptos-stdlib/sources/type_info.move b/aptos-move/framework/aptos-stdlib/sources/type_info.move
index 2989c0529d80e..5bdc186a31dd6 100644
--- a/aptos-move/framework/aptos-stdlib/sources/type_info.move
+++ b/aptos-move/framework/aptos-stdlib/sources/type_info.move
@@ -1,4 +1,6 @@
 module aptos_std::type_info {
+    use std::string;
+
     struct TypeInfo has copy, drop, store {
         account_address: address,
         module_name: vector<u8>,
@@ -18,6 +20,7 @@ module aptos_std::type_info {
     }
 
     public native fun type_of<T>(): TypeInfo;
+    public native fun type_name<T>(): string::String;
 
     #[test]
     fun test() {
@@ -26,4 +29,31 @@ module aptos_std::type_info {
         assert!(module_name(&type_info) == b"type_info", 1);
         assert!(struct_name(&type_info) == b"TypeInfo", 2);
     }
+
+    #[test]
+    fun test_type_name() {
+        use aptos_std::table::Table;
+
+        assert!(type_name<bool>() == string::utf8(b"bool"), 0);
+        assert!(type_name<u8>() == string::utf8(b"u8"), 1);
+        assert!(type_name<u64>() == string::utf8(b"u64"), 2);
+        assert!(type_name<u128>() == string::utf8(b"u128"), 3);
+        assert!(type_name<address>() == string::utf8(b"address"), 4);
+        assert!(type_name<signer>() == string::utf8(b"signer"), 5);
+
+        // vector
+        assert!(type_name<vector<u8>>() == string::utf8(b"vector<u8>"), 6);
+        assert!(type_name<vector<vector<u8>>>() == string::utf8(b"vector<vector<u8>>"), 7);
+        assert!(type_name<vector<vector<TypeInfo>>>() == string::utf8(b"vector<vector<0x1::type_info::TypeInfo>>"), 8);
+
+
+        // struct
+        assert!(type_name<TypeInfo>() == string::utf8(b"0x1::type_info::TypeInfo"), 9);
+        assert!(type_name<
+            Table<
+                TypeInfo,
+                Table<u8, vector<TypeInfo>>
+            >
+        >() == string::utf8(b"0x1::table::Table<0x1::type_info::TypeInfo, 0x1::table::Table<u8, vector<0x1::type_info::TypeInfo>>>"), 10);
+    }
 }
diff --git a/aptos-move/framework/src/natives/mod.rs b/aptos-move/framework/src/natives/mod.rs
index 0b42d10a9d96d..1c74fa539e53c 100644
--- a/aptos-move/framework/src/natives/mod.rs
+++ b/aptos-move/framework/src/natives/mod.rs
@@ -14,6 +14,7 @@ use move_deps::{
 pub mod cost {
     pub const APTOS_CREATE_ADDRESS: u64 = 5;
     pub const APTOS_LIB_TYPE_OF: u64 = 10;
+    pub const APTOS_LIB_TYPE_NAME: u64 = 10;
     pub const APTOS_SIP_HASH: u64 = 10;
     pub const APTOS_SECP256K1_RECOVER: u64 = 71;
 }
@@ -50,6 +51,7 @@ pub fn all_natives(framework_addr: AccountAddress) -> NativeFunctionTable {
             signature::native_secp256k1_recover,
         ),
         ("type_info", "type_of", type_info::type_of),
+        ("type_info", "type_name", type_info::type_name),
         ("hash", "sip_hash", hash::native_sip_hash),
     ];
     NATIVES
diff --git a/aptos-move/framework/src/natives/type_info.rs b/aptos-move/framework/src/natives/type_info.rs
index 6367ac85cc56d..d475e56e9e6d4 100644
--- a/aptos-move/framework/src/natives/type_info.rs
+++ b/aptos-move/framework/src/natives/type_info.rs
@@ -29,6 +29,7 @@ pub fn type_of(
     let cost = GasCost::new(super::cost::APTOS_LIB_TYPE_OF, 1).total();
 
     let type_tag = context.type_to_type_tag(&ty_args[0])?;
+
     if let TypeTag::Struct(struct_tag) = type_tag {
         Ok(NativeResult::ok(
             cost,
@@ -42,6 +43,27 @@ pub fn type_of(
     }
 }
 
+/// Returns a string representing the TypeTag of the parameter.
+pub fn type_name(
+    context: &mut NativeContext,
+    ty_args: Vec<Type>,
+    arguments: VecDeque<Value>,
+) -> PartialVMResult<NativeResult> {
+    debug_assert!(ty_args.len() == 1);
+    debug_assert!(arguments.is_empty());
+
+    let cost = GasCost::new(super::cost::APTOS_LIB_TYPE_NAME, 1).total();
+    let type_tag = context.type_to_type_tag(&ty_args[0])?;
+    let type_name = type_tag.to_string();
+
+    Ok(NativeResult::ok(
+        cost,
+        smallvec![Value::struct_(Struct::pack(vec![Value::vector_u8(
+            type_name.as_bytes().to_vec()
+        )]))],
+    ))
+}
+
 fn type_of_internal(struct_tag: &StructTag) -> Result<SmallVec<[Value; 1]>, std::fmt::Error> {
     let mut name = struct_tag.name.to_string();
     if let Some(first_ty) = struct_tag.type_params.first() {
diff --git a/aptos-move/framework/tests/move_unit_test.rs b/aptos-move/framework/tests/move_unit_test.rs
index 075d1cffb5da2..0af58ac547e2d 100644
--- a/aptos-move/framework/tests/move_unit_test.rs
+++ b/aptos-move/framework/tests/move_unit_test.rs
@@ -41,10 +41,15 @@ pub fn aptos_test_natives() -> NativeFunctionTable {
 }
 
 #[test]
-fn move_unit_tests() {
+fn move_framework_unit_tests() {
     run_tests_for_pkg("aptos-framework");
 }
 
+#[test]
+fn move_stdlib_unit_tests() {
+    run_tests_for_pkg("aptos-stdlib");
+}
+
 #[test]
 fn move_token_unit_tests() {
     run_tests_for_pkg("aptos-token");
diff --git a/testsuite/run_forge.sh b/testsuite/run_forge.sh
index 4c503081d3053..f186c8a0719d1 100755
--- a/testsuite/run_forge.sh
+++ b/testsuite/run_forge.sh
@@ -14,7 +14,7 @@ pwd | grep -qE 'aptos-core$' || (echo "Please run from aptos-core root directory
 
 # for calculating regression
 TPS_THRESHOLD=5000
-P99_LATENCY_MS_THRESHOLD=6000
+P99_LATENCY_MS_THRESHOLD=8000
 
 FORGE_OUTPUT=${FORGE_OUTPUT:-forge_output.txt}
 FORGE_REPORT=${FORGE_REPORT:-forge_report.json}