diff --git a/crates/aptos-api-tester/src/consts.rs b/crates/aptos-api-tester/src/consts.rs
new file mode 100644
index 0000000000000..ef59c196dddc9
--- /dev/null
+++ b/crates/aptos-api-tester/src/consts.rs
@@ -0,0 +1,55 @@
+// Copyright © Aptos Foundation
+
+use once_cell::sync::Lazy;
+use std::{env, time::Duration};
+use url::Url;
+
+// faucet constants
+
+pub static DEVNET_NODE_URL: Lazy<Url> =
+    Lazy::new(|| Url::parse("https://fullnode.devnet.aptoslabs.com").unwrap());
+
+pub static DEVNET_FAUCET_URL: Lazy<Url> =
+    Lazy::new(|| Url::parse("https://faucet.devnet.aptoslabs.com").unwrap());
+
+pub static TESTNET_NODE_URL: Lazy<Url> =
+    Lazy::new(|| Url::parse("https://fullnode.testnet.aptoslabs.com").unwrap());
+
+pub static TESTNET_FAUCET_URL: Lazy<Url> =
+    Lazy::new(|| Url::parse("https://faucet.testnet.aptoslabs.com").unwrap());
+
+pub const FUND_AMOUNT: u64 = 100_000_000;
+
+// persistency check constants
+
+pub static PERSISTENCY_TIMEOUT: Lazy<Duration> = Lazy::new(|| {
+    env::var("PERSISTENCY_TIMEOUT")
+        .ok()
+        .and_then(|s| s.parse().ok())
+        .map(Duration::from_secs)
+        .unwrap_or(Duration::from_secs(30))
+});
+
+pub static SLEEP_PER_CYCLE: Lazy<Duration> = Lazy::new(|| {
+    env::var("SLEEP_PER_CYCLE")
+        .ok()
+        .and_then(|s| s.parse().ok())
+        .map(Duration::from_millis)
+        .unwrap_or(Duration::from_millis(100))
+});
+
+// runtime constants
+
+pub static NUM_THREADS: Lazy<usize> = Lazy::new(|| {
+    env::var("NUM_THREADS")
+        .ok()
+        .and_then(|s| s.parse().ok())
+        .unwrap_or(4)
+});
+
+pub static STACK_SIZE: Lazy<usize> = Lazy::new(|| {
+    env::var("STACK_SIZE")
+        .ok()
+        .and_then(|s| s.parse().ok())
+        .unwrap_or(4 * 1024 * 1024)
+});
diff --git a/crates/aptos-api-tester/src/counters.rs b/crates/aptos-api-tester/src/counters.rs
index 62441749d83a9..1f6305dd644b6 100644
--- a/crates/aptos-api-tester/src/counters.rs
+++ b/crates/aptos-api-tester/src/counters.rs
@@ -54,3 +54,22 @@ pub static API_TEST_LATENCY: Lazy<HistogramVec> = Lazy::new(|| {
 pub fn test_latency(test_name: &str, network_name: &str, run_id: &str, result: &str) -> Histogram {
     API_TEST_LATENCY.with_label_values(&[test_name, network_name, run_id, result])
 }
+
+pub static API_TEST_STEP_LATENCY: Lazy<HistogramVec> = Lazy::new(|| {
+    register_histogram_vec!(
+        "api_test_step_latency",
+        "Time it takes to complete a user flow step",
+        &["test_name", "step_name", "network_name", "run_id", "result"],
+    )
+    .unwrap()
+});
+
+pub fn test_step_latency(
+    test_name: &str,
+    step_name: &str,
+    network_name: &str,
+    run_id: &str,
+    result: &str,
+) -> Histogram {
+    API_TEST_STEP_LATENCY.with_label_values(&[test_name, step_name, network_name, run_id, result])
+}
diff --git a/crates/aptos-api-tester/src/fail_message.rs b/crates/aptos-api-tester/src/fail_message.rs
index 5e271cc2147ca..9d4065e5050cf 100644
--- a/crates/aptos-api-tester/src/fail_message.rs
+++ b/crates/aptos-api-tester/src/fail_message.rs
@@ -1,27 +1,27 @@
 // Copyright © Aptos Foundation
 
-pub static FAIL_WRONG_ACCOUNT_DATA: &str = "wrong account data";
-pub static FAIL_WRONG_BALANCE: &str = "wrong balance";
-pub static FAIL_WRONG_BALANCE_AT_VERSION: &str = "wrong balance at version";
-pub static FAIL_WRONG_COLLECTION_DATA: &str = "wrong collection data";
-pub static FAIL_WRONG_MESSAGE: &str = "wrong message";
-pub static FAIL_WRONG_MODULE: &str = "wrong module";
-pub static FAIL_WRONG_TOKEN_BALANCE: &str = "wrong token balance";
-pub static FAIL_WRONG_TOKEN_DATA: &str = "wrong token data";
-pub static ERROR_COULD_NOT_BUILD_PACKAGE: &str = "failed to build package";
-pub static ERROR_COULD_NOT_CHECK: &str = "persistency check never started";
-pub static ERROR_COULD_NOT_CREATE_ACCOUNT: &str = "failed to create account";
-pub static ERROR_COULD_NOT_CREATE_TRANSACTION: &str = "failed to create transaction";
-pub static ERROR_COULD_NOT_FINISH_TRANSACTION: &str = "failed to finish transaction";
-pub static ERROR_COULD_NOT_FUND_ACCOUNT: &str = "failed to fund account";
-pub static ERROR_COULD_NOT_SERIALIZE: &str = "failed to serialize";
-pub static ERROR_NO_ACCOUNT_DATA: &str = "can't find account data";
-pub static ERROR_NO_BALANCE: &str = "can't find account balance";
-pub static ERROR_NO_BYTECODE: &str = "can't find bytecode";
-pub static ERROR_NO_COLLECTION_DATA: &str = "can't find collection data";
-pub static ERROR_NO_MESSAGE: &str = "can't find message";
-pub static ERROR_NO_METADATA: &str = "can't find metadata";
-pub static ERROR_NO_MODULE: &str = "can't find module";
-pub static ERROR_NO_TOKEN_BALANCE: &str = "can't find token balance";
-pub static ERROR_NO_TOKEN_DATA: &str = "can't find token data";
-pub static ERROR_NO_VERSION: &str = "can't find transaction version";
+pub const FAIL_WRONG_ACCOUNT_DATA: &str = "wrong account data";
+pub const FAIL_WRONG_BALANCE: &str = "wrong balance";
+pub const FAIL_WRONG_BALANCE_AT_VERSION: &str = "wrong balance at version";
+pub const FAIL_WRONG_COLLECTION_DATA: &str = "wrong collection data";
+pub const FAIL_WRONG_MESSAGE: &str = "wrong message";
+pub const FAIL_WRONG_MODULE: &str = "wrong module";
+pub const FAIL_WRONG_TOKEN_BALANCE: &str = "wrong token balance";
+pub const FAIL_WRONG_TOKEN_DATA: &str = "wrong token data";
+pub const ERROR_COULD_NOT_BUILD_PACKAGE: &str = "failed to build package";
+pub const ERROR_COULD_NOT_CHECK: &str = "persistency check never started";
+pub const ERROR_COULD_NOT_CREATE_ACCOUNT: &str = "failed to create account";
+pub const ERROR_COULD_NOT_CREATE_TRANSACTION: &str = "failed to create transaction";
+pub const ERROR_COULD_NOT_FINISH_TRANSACTION: &str = "failed to finish transaction";
+pub const ERROR_COULD_NOT_FUND_ACCOUNT: &str = "failed to fund account";
+pub const ERROR_COULD_NOT_SERIALIZE: &str = "failed to serialize";
+pub const ERROR_NO_ACCOUNT_DATA: &str = "can't find account data";
+pub const ERROR_NO_BALANCE: &str = "can't find account balance";
+pub const ERROR_NO_BYTECODE: &str = "can't find bytecode";
+pub const ERROR_NO_COLLECTION_DATA: &str = "can't find collection data";
+pub const ERROR_NO_MESSAGE: &str = "can't find message";
+pub const ERROR_NO_METADATA: &str = "can't find metadata";
+pub const ERROR_NO_MODULE: &str = "can't find module";
+pub const ERROR_NO_TOKEN_BALANCE: &str = "can't find token balance";
+pub const ERROR_NO_TOKEN_DATA: &str = "can't find token data";
+pub const ERROR_NO_VERSION: &str = "can't find transaction version";
diff --git a/crates/aptos-api-tester/src/macros.rs b/crates/aptos-api-tester/src/macros.rs
new file mode 100644
index 0000000000000..f40d2fd093b58
--- /dev/null
+++ b/crates/aptos-api-tester/src/macros.rs
@@ -0,0 +1,18 @@
+// Copyright © Aptos Foundation
+
+#[macro_export]
+macro_rules! time_fn {
+    ($func:expr, $($arg:expr), *) => {{
+        // start timer
+        let start = tokio::time::Instant::now();
+
+        // call the flow
+        let result = $func($($arg),+).await;
+
+        // end timer
+        let time = (tokio::time::Instant::now() - start).as_micros() as f64;
+
+        // return
+        (result, time)
+    }};
+}
diff --git a/crates/aptos-api-tester/src/main.rs b/crates/aptos-api-tester/src/main.rs
index 8a555f112d916..07079aaadd0e8 100644
--- a/crates/aptos-api-tester/src/main.rs
+++ b/crates/aptos-api-tester/src/main.rs
@@ -3,135 +3,86 @@
 
 #![forbid(unsafe_code)]
 
+mod consts;
 mod counters;
 mod fail_message;
 mod persistent_check;
 mod tests;
 mod utils;
+#[macro_use]
+mod macros;
 
-use crate::{
-    tests::{coin_transfer, new_account, nft_transfer, publish_module},
-    utils::{set_metrics, NetworkName, TestFailure, TestName, TestResult},
-};
+use crate::utils::{NetworkName, TestName};
 use anyhow::Result;
 use aptos_logger::{info, Level, Logger};
 use aptos_push_metrics::MetricsPusher;
+use consts::{NUM_THREADS, STACK_SIZE};
 use futures::future::join_all;
-use std::{
-    future::Future,
-    time::{Instant, SystemTime, UNIX_EPOCH},
-};
+use std::time::{SystemTime, UNIX_EPOCH};
+use tokio::runtime::{Builder, Runtime};
 
-// Processes a test result.
-async fn process_result<Fut: Future<Output = Result<(), TestFailure>>>(
-    test_name: TestName,
-    network_name: NetworkName,
-    run_id: &str,
-    fut: Fut,
-) {
-    // start timer
-    let start = Instant::now();
-
-    // call the flow
-    let result = fut.await;
-
-    // end timer
-    let time = (Instant::now() - start).as_micros() as f64;
-
-    // process the result
-    let output = match result {
-        Ok(_) => TestResult::Success,
-        Err(failure) => TestResult::from(failure),
-    };
-
-    // set metrics and log
-    set_metrics(
-        &output,
-        &test_name.to_string(),
-        &network_name.to_string(),
-        run_id,
-        time,
-    );
-    info!(
-        "{} {} result:{:?} in time:{:?}",
-        network_name.to_string(),
-        test_name.to_string(),
-        output,
-        time,
-    );
-}
-
-async fn test_flows(network_name: NetworkName) -> Result<()> {
+async fn test_flows(runtime: &Runtime, network_name: NetworkName) -> Result<()> {
     let run_id = SystemTime::now()
         .duration_since(UNIX_EPOCH)?
         .as_secs()
         .to_string();
-    info!("testing {} at {}", network_name.to_string(), run_id);
+    info!(
+        "----- STARTING TESTS FOR {} WITH RUN ID {} -----",
+        network_name.to_string(),
+        run_id
+    );
 
-    // Test new account creation and funding
+    // Flow 1: New account
     let test_time = run_id.clone();
-    let handle_newaccount = tokio::spawn(async move {
-        process_result(
-            TestName::NewAccount,
-            network_name,
-            &test_time,
-            new_account::test(network_name),
-        )
-        .await;
+    let handle_newaccount = runtime.spawn(async move {
+        TestName::NewAccount.run(network_name, &test_time).await;
     });
 
-    // Flow 1: Coin transfer
+    // Flow 2: Coin transfer
     let test_time = run_id.clone();
-    let handle_cointransfer = tokio::spawn(async move {
-        process_result(
-            TestName::CoinTransfer,
-            network_name,
-            &test_time,
-            coin_transfer::test(network_name),
-        )
-        .await;
+    let handle_cointransfer = runtime.spawn(async move {
+        TestName::CoinTransfer.run(network_name, &test_time).await;
     });
 
-    // Flow 2: NFT transfer
+    // Flow 3: NFT transfer
     let test_time = run_id.clone();
-    let handle_nfttransfer = tokio::spawn(async move {
-        process_result(
-            TestName::NftTransfer,
-            network_name,
-            &test_time,
-            nft_transfer::test(network_name),
-        )
-        .await;
+    let handle_nfttransfer = runtime.spawn(async move {
+        TestName::NftTransfer.run(network_name, &test_time).await;
     });
 
-    // Flow 3: Publishing module
+    // Flow 4: Publishing module
     let test_time = run_id.clone();
-    process_result(
-        TestName::PublishModule,
-        network_name,
-        &test_time,
-        publish_module::test(network_name),
-    )
-    .await;
+    let handle_publishmodule = runtime.spawn(async move {
+        TestName::PublishModule.run(network_name, &test_time).await;
+    });
 
     join_all(vec![
         handle_newaccount,
         handle_cointransfer,
         handle_nfttransfer,
+        handle_publishmodule,
     ])
     .await;
     Ok(())
 }
 
-#[tokio::main]
-async fn main() -> Result<()> {
+fn main() -> Result<()> {
+    // create runtime
+    let runtime = Builder::new_multi_thread()
+        .worker_threads(*NUM_THREADS)
+        .enable_all()
+        .thread_stack_size(*STACK_SIZE)
+        .build()?;
+
     // log metrics
     Logger::builder().level(Level::Info).build();
     let _mp = MetricsPusher::start_for_local_run("api-tester");
 
-    // test flows
-    let _ = test_flows(NetworkName::Testnet).await;
-    let _ = test_flows(NetworkName::Devnet).await;
+    // run tests
+    runtime.block_on(async {
+        let _ = test_flows(&runtime, NetworkName::Testnet).await;
+        let _ = test_flows(&runtime, NetworkName::Devnet).await;
+    });
 
     Ok(())
 }
diff --git a/crates/aptos-api-tester/src/persistent_check.rs b/crates/aptos-api-tester/src/persistent_check.rs
index 9e1873af2cc27..5eeae79f141ca 100644
--- a/crates/aptos-api-tester/src/persistent_check.rs
+++ b/crates/aptos-api-tester/src/persistent_check.rs
@@ -1,16 +1,17 @@
 // Copyright © Aptos Foundation
 
-use crate::{fail_message::ERROR_COULD_NOT_CHECK, utils::TestFailure};
+use crate::{
+    consts::{PERSISTENCY_TIMEOUT, SLEEP_PER_CYCLE},
+    fail_message::ERROR_COULD_NOT_CHECK,
+    utils::TestFailure,
+};
 use anyhow::anyhow;
 use aptos_api_types::HexEncodedBytes;
 use aptos_rest_client::Client;
 use aptos_sdk::{token_client::TokenClient, types::LocalAccount};
 use aptos_types::account_address::AccountAddress;
 use futures::Future;
-use std::time::Duration;
-use tokio::time::Instant;
-
-static PERSISTENCY_TIMEOUT: Duration = Duration::from_secs(30);
+use tokio::time::{sleep, Instant};
 
 pub async fn account<'a, 'b, F, Fut>(
     step: &str,
@@ -27,11 +28,12 @@ where
     let timer = Instant::now();
 
     // try to get a good result
-    while Instant::now().duration_since(timer) < PERSISTENCY_TIMEOUT {
+    while Instant::now().duration_since(timer) < *PERSISTENCY_TIMEOUT {
         result = f(client, account).await;
         if result.is_ok() {
             break;
         }
+        sleep(*SLEEP_PER_CYCLE).await;
     }
 
     // return last failure if no good result occurs
@@ -53,11 +55,40 @@ where
     let timer = Instant::now();
 
     // try to get a good result
-    while Instant::now().duration_since(timer) < PERSISTENCY_TIMEOUT {
+    while Instant::now().duration_since(timer) < *PERSISTENCY_TIMEOUT {
         result = f(client, address).await;
         if result.is_ok() {
             break;
         }
+        sleep(*SLEEP_PER_CYCLE).await;
+    }
+
+    // return last failure if no good result occurs
+    result
+}
+
+pub async fn address_address<'a, F, Fut>(
+    step: &str,
+    f: F,
+    client: &'a Client,
+    address: AccountAddress,
+    address2: AccountAddress,
+) -> Result<(), TestFailure>
+where
+    F: Fn(&'a Client, AccountAddress, AccountAddress) -> Fut,
+    Fut: Future<Output = Result<(), TestFailure>>,
+{
+    // set a default error in case checks never start
+    let mut result: Result<(), TestFailure> = Err(could_not_check(step));
+    let timer = Instant::now();
+
+    // try to get a good result
+    while Instant::now().duration_since(timer) < *PERSISTENCY_TIMEOUT {
+        result = f(client, address, address2).await;
+        if result.is_ok() {
+            break;
+        }
+        sleep(*SLEEP_PER_CYCLE).await;
     }
 
     // return last failure if no good result occurs
@@ -80,11 +111,12 @@ where
     let timer = Instant::now();
 
     // try to get a good result
-    while Instant::now().duration_since(timer) < PERSISTENCY_TIMEOUT {
+    while Instant::now().duration_since(timer) < *PERSISTENCY_TIMEOUT {
         result = f(client, address, bytes).await;
         if result.is_ok() {
             break;
         }
+        sleep(*SLEEP_PER_CYCLE).await;
     }
 
     // return last failure if no good result occurs
@@ -107,11 +139,12 @@ where
     let timer = Instant::now();
 
     // try to get a good result
-    while Instant::now().duration_since(timer) < PERSISTENCY_TIMEOUT {
+    while Instant::now().duration_since(timer) < *PERSISTENCY_TIMEOUT {
         result = f(client, address, version).await;
         if result.is_ok() {
             break;
         }
+        sleep(*SLEEP_PER_CYCLE).await;
     }
 
     // return last failure if no good result occurs
@@ -133,11 +166,12 @@ where
     let timer = Instant::now();
 
     // try to get a good result
-    while Instant::now().duration_since(timer) < PERSISTENCY_TIMEOUT {
+    while Instant::now().duration_since(timer) < *PERSISTENCY_TIMEOUT {
         result = f(token_client, address).await;
         if result.is_ok() {
             break;
         }
+        sleep(*SLEEP_PER_CYCLE).await;
     }
 
     // return last failure if no good result occurs
@@ -160,11 +194,12 @@ where
     let timer = Instant::now();
 
     // try to get a good result
-    while Instant::now().duration_since(timer) < PERSISTENCY_TIMEOUT {
+    while Instant::now().duration_since(timer) < *PERSISTENCY_TIMEOUT {
         result = f(token_client, address, address2).await;
         if result.is_ok() {
             break;
         }
+        sleep(*SLEEP_PER_CYCLE).await;
     }
 
     // return last failure if no good result occurs
diff --git a/crates/aptos-api-tester/src/tests/coin_transfer.rs b/crates/aptos-api-tester/src/tests/coin_transfer.rs
index 4a23fa331215b..38903c5149a19 100644
--- a/crates/aptos-api-tester/src/tests/coin_transfer.rs
+++ b/crates/aptos-api-tester/src/tests/coin_transfer.rs
@@ -1,15 +1,16 @@
 // Copyright © Aptos Foundation
 
 use crate::{
+    consts::FUND_AMOUNT,
     fail_message::{
         ERROR_COULD_NOT_CREATE_ACCOUNT, ERROR_COULD_NOT_CREATE_TRANSACTION,
         ERROR_COULD_NOT_FINISH_TRANSACTION, ERROR_COULD_NOT_FUND_ACCOUNT, ERROR_NO_BALANCE,
         ERROR_NO_VERSION, FAIL_WRONG_BALANCE, FAIL_WRONG_BALANCE_AT_VERSION,
     },
-    persistent_check,
+    persistent_check, time_fn,
     utils::{
-        create_account, create_and_fund_account, get_client, get_faucet_client, NetworkName,
-        TestFailure,
+        check_balance, create_account, create_and_fund_account, emit_step_metrics, get_client,
+        get_faucet_client, NetworkName, TestFailure, TestName,
     },
 };
 use anyhow::{anyhow, Result};
@@ -24,32 +25,78 @@ static TRANSFER_AMOUNT: u64 = 1_000;
 /// Tests coin transfer. Checks that:
 ///   - receiver balance reflects transferred amount
 ///   - receiver balance shows correct amount at the previous version
-pub async fn test(network_name: NetworkName) -> Result<(), TestFailure> {
+pub async fn test(network_name: NetworkName, run_id: &str) -> Result<(), TestFailure> {
     // setup
-    let (client, mut account, receiver) = setup(network_name).await?;
+    let (client, mut account, receiver) = emit_step_metrics(
+        time_fn!(setup, network_name),
+        TestName::CoinTransfer,
+        "setup",
+        network_name,
+        run_id,
+    )?;
     let coin_client = CoinClient::new(&client);
 
+    // check account data persistently
+    emit_step_metrics(
+        time_fn!(
+            persistent_check::address_address,
+            "check_account_data",
+            check_account_data,
+            &client,
+            account.address(),
+            receiver
+        ),
+        TestName::CoinTransfer,
+        "check_account_data",
+        network_name,
+        run_id,
+    )?;
+
     // transfer coins to the receiver
-    let version = transfer_coins(&client, &coin_client, &mut account, receiver).await?;
+    let version = emit_step_metrics(
+        time_fn!(
+            transfer_coins,
+            &client,
+            &coin_client,
+            &mut account,
+            receiver
+        ),
+        TestName::CoinTransfer,
+        "transfer_coins",
+        network_name,
+        run_id,
+    )?;
 
     // check receiver balance persistently
-    persistent_check::address(
+    emit_step_metrics(
+        time_fn!(
+            persistent_check::address,
+            "check_account_balance",
+            check_account_balance,
+            &client,
+            receiver
+        ),
+        TestName::CoinTransfer,
         "check_account_balance",
-        check_account_balance,
-        &client,
-        receiver,
-    )
-    .await?;
+        network_name,
+        run_id,
+    )?;
 
     // check receiver balance at previous version persistently
-    persistent_check::address_version(
+    emit_step_metrics(
+        time_fn!(
+            persistent_check::address_version,
+            "check_account_balance_at_version",
+            check_account_balance_at_version,
+            &client,
+            receiver,
+            version
+        ),
+        TestName::CoinTransfer,
         "check_account_balance_at_version",
-        check_account_balance_at_version,
-        &client,
-        receiver,
-        version,
-    )
-    .await?;
+        network_name,
+        run_id,
+    )?;
 
     Ok(())
 }
@@ -74,6 +121,10 @@ async fn setup(
             return Err(e.into());
         },
     };
+    info!(
+        "test: coin_transfer part: setup creating account: {}",
+        account.address()
+    );
 
     // create receiver
     let receiver = match create_account(&faucet_client).await {
@@ -86,10 +137,25 @@ async fn setup(
             return Err(e.into());
         },
     };
+    info!(
+        "test: coin_transfer part: setup creating receiver: {}",
+        receiver
+    );
 
     Ok((client, account, receiver))
 }
 
+async fn check_account_data(
+    client: &Client,
+    account: AccountAddress,
+    receiver: AccountAddress,
+) -> Result<(), TestFailure> {
+    check_balance(TestName::CoinTransfer, client, account, U64(FUND_AMOUNT)).await?;
+    check_balance(TestName::CoinTransfer, client, receiver, U64(0)).await?;
+
+    Ok(())
+}
+
 async fn transfer_coins(
     client: &Client,
     coin_client: &CoinClient<'_>,
diff --git a/crates/aptos-api-tester/src/tests/new_account.rs b/crates/aptos-api-tester/src/tests/new_account.rs
index 4c1a557f00b4d..14b5e8b964b2a 100644
--- a/crates/aptos-api-tester/src/tests/new_account.rs
+++ b/crates/aptos-api-tester/src/tests/new_account.rs
@@ -1,12 +1,16 @@
 // Copyright © Aptos Foundation
 
 use crate::{
+    consts::FUND_AMOUNT,
     fail_message::{
         ERROR_COULD_NOT_CREATE_ACCOUNT, ERROR_COULD_NOT_FUND_ACCOUNT, ERROR_NO_ACCOUNT_DATA,
-        ERROR_NO_BALANCE, FAIL_WRONG_ACCOUNT_DATA, FAIL_WRONG_BALANCE,
+        FAIL_WRONG_ACCOUNT_DATA,
+    },
+    persistent_check, time_fn,
+    utils::{
+        check_balance, create_account, emit_step_metrics, get_client, get_faucet_client,
+        NetworkName, TestFailure, TestName,
     },
-    persistent_check,
-    utils::{create_account, get_client, get_faucet_client, NetworkName, TestFailure},
 };
 use aptos_api_types::U64;
 use aptos_logger::info;
@@ -14,29 +18,57 @@ use aptos_rest_client::{Account, Client, FaucetClient};
 use aptos_sdk::types::LocalAccount;
 use aptos_types::account_address::AccountAddress;
 
-static FUND_AMOUNT: u64 = 1_000_000;
-
 /// Tests new account creation. Checks that:
 ///   - account data exists
 ///   - account balance reflects funded amount
-pub async fn test(network_name: NetworkName) -> Result<(), TestFailure> {
+pub async fn test(network_name: NetworkName, run_id: &str) -> Result<(), TestFailure> {
     // setup
-    let (client, faucet_client, account) = setup(network_name).await?;
+    let (client, faucet_client, account) = emit_step_metrics(
+        time_fn!(setup, network_name),
+        TestName::NewAccount,
+        "setup",
+        network_name,
+        run_id,
+    )?;
 
     // check account data persistently
-    persistent_check::account("check_account_data", check_account_data, &client, &account).await?;
+    emit_step_metrics(
+        time_fn!(
+            persistent_check::account,
+            "check_account_data",
+            check_account_data,
+            &client,
+            &account
+        ),
+        TestName::NewAccount,
+        "check_account_data",
+        network_name,
+        run_id,
+    )?;
 
     // fund account
-    fund(&faucet_client, account.address()).await?;
+    emit_step_metrics(
+        time_fn!(fund, &faucet_client, account.address()),
+        TestName::NewAccount,
+        "fund",
+        network_name,
+        run_id,
+    )?;
 
     // check account balance persistently
-    persistent_check::address(
+    emit_step_metrics(
+        time_fn!(
+            persistent_check::address,
+            "check_account_balance",
+            check_account_balance,
+            &client,
+            account.address()
+        ),
+        TestName::NewAccount,
         "check_account_balance",
-        check_account_balance,
-        &client,
-        account.address(),
-    )
-    .await?;
+        network_name,
+        run_id,
+    )?;
 
     Ok(())
 }
@@ -61,6 +93,10 @@ async fn setup(
             return Err(e.into());
         },
     };
+    info!(
+        "test: new_account part: setup creating account: {}",
+        account.address()
+    );
 
     Ok((client, faucet_client, account))
 }
@@ -113,29 +149,5 @@ async fn check_account_balance(
     client: &Client,
     address: AccountAddress,
 ) -> Result<(), TestFailure> {
-    // expected
-    let expected = U64(FUND_AMOUNT);
-
-    // actual
-    let actual = match client.get_account_balance(address).await {
-        Ok(response) => response.into_inner().coin.value,
-        Err(e) => {
-            info!(
-                "test: new_account part: check_account_balance ERROR: {}, with error {:?}",
-                ERROR_NO_BALANCE, e
-            );
-            return Err(e.into());
-        },
-    };
-
-    // compare
-    if expected != actual {
-        info!(
-            "test: new_account part: check_account_balance FAIL: {}, expected {:?}, got {:?}",
-            FAIL_WRONG_BALANCE, expected, actual
-        );
-        return Err(TestFailure::Fail(FAIL_WRONG_BALANCE));
-    }
-
-    Ok(())
+    check_balance(TestName::NewAccount, client, address, U64(FUND_AMOUNT)).await
 }
diff --git a/crates/aptos-api-tester/src/tests/nft_transfer.rs b/crates/aptos-api-tester/src/tests/nft_transfer.rs
index 541966d703d42..ebf79c9b12f74 100644
--- a/crates/aptos-api-tester/src/tests/nft_transfer.rs
+++ b/crates/aptos-api-tester/src/tests/nft_transfer.rs
@@ -1,14 +1,18 @@
 // Copyright © Aptos Foundation
 
 use crate::{
+    consts::FUND_AMOUNT,
     fail_message::{
         ERROR_COULD_NOT_CREATE_TRANSACTION, ERROR_COULD_NOT_FINISH_TRANSACTION,
         ERROR_COULD_NOT_FUND_ACCOUNT, ERROR_NO_COLLECTION_DATA, ERROR_NO_TOKEN_BALANCE,
         ERROR_NO_TOKEN_DATA, FAIL_WRONG_COLLECTION_DATA, FAIL_WRONG_TOKEN_BALANCE,
         FAIL_WRONG_TOKEN_DATA,
     },
-    persistent_check,
-    utils::{create_and_fund_account, get_client, get_faucet_client, NetworkName, TestFailure},
+    persistent_check, time_fn,
+    utils::{
+        check_balance, create_and_fund_account, emit_step_metrics, get_client, get_faucet_client,
+        NetworkName, TestFailure, TestName,
+    },
 };
 use aptos_api_types::U64;
 use aptos_logger::info;
@@ -31,59 +35,141 @@ static OFFER_AMOUNT: u64 = 2;
 ///   - collection data exists
 ///   - token data exists
 ///   - token balance reflects transferred amount
-pub async fn test(network_name: NetworkName) -> Result<(), TestFailure> {
+pub async fn test(network_name: NetworkName, run_id: &str) -> Result<(), TestFailure> {
     // setup
-    let (client, mut account, mut receiver) = setup(network_name).await?;
+    let (client, mut account, mut receiver) = emit_step_metrics(
+        time_fn!(setup, network_name),
+        TestName::NftTransfer,
+        "setup",
+        network_name,
+        run_id,
+    )?;
     let token_client = TokenClient::new(&client);
 
+    // check account data persistently
+    emit_step_metrics(
+        time_fn!(
+            persistent_check::address_address,
+            "check_account_data",
+            check_account_data,
+            &client,
+            account.address(),
+            receiver.address()
+        ),
+        TestName::NftTransfer,
+        "check_account_data",
+        network_name,
+        run_id,
+    )?;
+
     // create collection
-    create_collection(&client, &token_client, &mut account).await?;
+    emit_step_metrics(
+        time_fn!(create_collection, &client, &token_client, &mut account),
+        TestName::NftTransfer,
+        "create_collection",
+        network_name,
+        run_id,
+    )?;
 
     // check collection metadata persistently
-    persistent_check::token_address(
+    emit_step_metrics(
+        time_fn!(
+            persistent_check::token_address,
+            "check_collection_metadata",
+            check_collection_metadata,
+            &token_client,
+            account.address()
+        ),
+        TestName::NftTransfer,
         "check_collection_metadata",
-        check_collection_metadata,
-        &token_client,
-        account.address(),
-    )
-    .await?;
+        network_name,
+        run_id,
+    )?;
 
     // create token
-    create_token(&client, &token_client, &mut account).await?;
+    emit_step_metrics(
+        time_fn!(create_token, &client, &token_client, &mut account),
+        TestName::NftTransfer,
+        "create_token",
+        network_name,
+        run_id,
+    )?;
 
     // check token metadata persistently
-    persistent_check::token_address(
+    emit_step_metrics(
+        time_fn!(
+            persistent_check::token_address,
+            "check_token_address",
+            check_token_metadata,
+            &token_client,
+            account.address()
+        ),
+        TestName::NftTransfer,
         "check_token_metadata",
-        check_token_metadata,
-        &token_client,
-        account.address(),
-    )
-    .await?;
+        network_name,
+        run_id,
+    )?;
 
     // offer token
-    offer_token(&client, &token_client, &mut account, receiver.address()).await?;
+    emit_step_metrics(
+        time_fn!(
+            offer_token,
+            &client,
+            &token_client,
+            &mut account,
+            receiver.address()
+        ),
+        TestName::NftTransfer,
+        "offer_token",
+        network_name,
+        run_id,
+    )?;
 
     // check senders balance persistently
-    persistent_check::token_address(
+    emit_step_metrics(
+        time_fn!(
+            persistent_check::token_address,
+            "check_sender_balance",
+            check_sender_balance,
+            &token_client,
+            account.address()
+        ),
+        TestName::NftTransfer,
         "check_sender_balance",
-        check_sender_balance,
-        &token_client,
-        account.address(),
-    )
-    .await?;
+        network_name,
+        run_id,
+    )?;
 
     // claim token
-    claim_token(&client, &token_client, &mut receiver, account.address()).await?;
+    emit_step_metrics(
+        time_fn!(
+            claim_token,
+            &client,
+            &token_client,
+            &mut receiver,
+            account.address()
+        ),
+        TestName::NftTransfer,
+        "claim_token",
+        network_name,
+        run_id,
+    )?;
 
     // check receivers balance persistently
-    persistent_check::token_address_address(
+    emit_step_metrics(
+        time_fn!(
+            persistent_check::token_address_address,
+            "check_receiver_balance",
+            check_receiver_balance,
+            &token_client,
+            receiver.address(),
+            account.address()
+        ),
+        TestName::NftTransfer,
         "check_receiver_balance",
-        check_receiver_balance,
-        &token_client,
-        receiver.address(),
-        account.address(),
-    )
-    .await?;
+        network_name,
+        run_id,
+    )?;
 
     Ok(())
 }
@@ -108,6 +194,10 @@ async fn setup(
             return Err(e.into());
         },
     };
+    info!(
+        "test: nft_transfer part: setup creating account: {}",
+        account.address()
+    );
 
     // create receiver
     let receiver = match create_and_fund_account(&faucet_client).await {
@@ -120,10 +210,25 @@ async fn setup(
             return Err(e.into());
         },
     };
+    info!(
+        "test: nft_transfer part: setup creating receiver: {}",
+        receiver.address()
+    );
 
     Ok((client, account, receiver))
 }
 
+async fn check_account_data(
+    client: &Client,
+    account: AccountAddress,
+    receiver: AccountAddress,
+) -> Result<(), TestFailure> {
+    check_balance(TestName::NftTransfer, client, account, U64(FUND_AMOUNT)).await?;
+    check_balance(TestName::NftTransfer, client, receiver, U64(FUND_AMOUNT)).await?;
+
+    Ok(())
+}
+
 async fn create_collection(
     client: &Client,
     token_client: &TokenClient<'_>,
diff --git a/crates/aptos-api-tester/src/tests/publish_module.rs b/crates/aptos-api-tester/src/tests/publish_module.rs
index 08e4653e357e7..e121429551802 100644
--- a/crates/aptos-api-tester/src/tests/publish_module.rs
+++ b/crates/aptos-api-tester/src/tests/publish_module.rs
@@ -1,17 +1,21 @@
 // Copyright © Aptos Foundation
 
 use crate::{
+    consts::FUND_AMOUNT,
     fail_message::{
         ERROR_COULD_NOT_BUILD_PACKAGE, ERROR_COULD_NOT_CREATE_TRANSACTION,
         ERROR_COULD_NOT_FINISH_TRANSACTION, ERROR_COULD_NOT_FUND_ACCOUNT,
         ERROR_COULD_NOT_SERIALIZE, ERROR_NO_BYTECODE, ERROR_NO_MESSAGE, ERROR_NO_METADATA,
         ERROR_NO_MODULE, FAIL_WRONG_MESSAGE, FAIL_WRONG_MODULE,
     },
-    persistent_check,
-    utils::{create_and_fund_account, get_client, get_faucet_client, NetworkName, TestFailure},
+    persistent_check, time_fn,
+    utils::{
+        check_balance, create_and_fund_account, emit_step_metrics, get_client, get_faucet_client,
+        NetworkName, TestFailure, TestName,
+    },
 };
 use anyhow::{anyhow, Result};
-use aptos_api_types::HexEncodedBytes;
+use aptos_api_types::{HexEncodedBytes, U64};
 use aptos_cached_packages::aptos_stdlib::EntryFunctionCall;
 use aptos_framework::{BuildOptions, BuiltPackage};
 use aptos_logger::info;
@@ -31,31 +35,93 @@ use std::{collections::BTreeMap, path::PathBuf};
 static MODULE_NAME: &str = "message";
 static MESSAGE: &str = "test message";
 
-pub async fn test(network_name: NetworkName) -> Result<(), TestFailure> {
+/// Tests nft transfer. Checks that:
+///   - can publish module
+///   - module data exists
+///   - can interact with module
+///   - interaction is reflected correctly
+pub async fn test(network_name: NetworkName, run_id: &str) -> Result<(), TestFailure> {
     // setup
-    let (client, mut account) = setup(network_name).await?;
+    let (client, mut account) = emit_step_metrics(
+        time_fn!(setup, network_name),
+        TestName::PublishModule,
+        "setup",
+        network_name,
+        run_id,
+    )?;
+
+    // check account data persistently
+    emit_step_metrics(
+        time_fn!(
+            persistent_check::address,
+            "check_account_data",
+            check_account_data,
+            &client,
+            account.address()
+        ),
+        TestName::PublishModule,
+        "check_account_data",
+        network_name,
+        run_id,
+    )?;
 
     // build module
-    let package = build_module(account.address()).await?;
+    let package = emit_step_metrics(
+        time_fn!(build_module, account.address()),
+        TestName::PublishModule,
+        "build_module",
+        network_name,
+        run_id,
+    )?;
 
     // publish module
-    let blob = publish_module(&client, &mut account, package).await?;
+    let blob = emit_step_metrics(
+        time_fn!(publish_module, &client, &mut account, package),
+        TestName::PublishModule,
+        "publish_module",
+        network_name,
+        run_id,
+    )?;
 
     // check module data persistently
-    persistent_check::address_bytes(
+    emit_step_metrics(
+        time_fn!(
+            persistent_check::address_bytes,
+            "check_module_data",
+            check_module_data,
+            &client,
+            account.address(),
+            &blob
+        ),
+        TestName::PublishModule,
         "check_module_data",
-        check_module_data,
-        &client,
-        account.address(),
-        &blob,
-    )
-    .await?;
+        network_name,
+        run_id,
+    )?;
 
     // set message
-    set_message(&client, &mut account).await?;
+    emit_step_metrics(
+        time_fn!(set_message, &client, &mut account),
+        TestName::PublishModule,
+        "set_message",
+        network_name,
+        run_id,
+    )?;
 
     // check message persistently
-    persistent_check::address("check_message", check_message, &client, account.address()).await?;
+    emit_step_metrics(
+        time_fn!(
+            persistent_check::address,
+            "check_message",
+            check_message,
+            &client,
+            account.address()
+        ),
+        TestName::PublishModule,
+        "check_message",
+        network_name,
+        run_id,
+    )?;
 
     Ok(())
 }
@@ -78,10 +144,20 @@ async fn setup(network_name: NetworkName) -> Result<(Client, LocalAccount), Test
             return Err(e.into());
         },
     };
+    info!(
+        "test: publish_module part: setup creating account: {}",
+        account.address()
+    );
 
     Ok((client, account))
 }
 
+async fn check_account_data(client: &Client, account: AccountAddress) -> Result<(), TestFailure> {
+    check_balance(TestName::PublishModule, client, account, U64(FUND_AMOUNT)).await?;
+
+    Ok(())
+}
+
 async fn build_module(address: AccountAddress) -> Result<BuiltPackage, TestFailure> {
     // get file to compile
     let move_dir = PathBuf::from("./aptos-move/move-examples/hello_blockchain");
diff --git a/crates/aptos-api-tester/src/utils.rs b/crates/aptos-api-tester/src/utils.rs
index eb85d94578002..db465e2e0c943 100644
--- a/crates/aptos-api-tester/src/utils.rs
+++ b/crates/aptos-api-tester/src/utils.rs
@@ -1,38 +1,23 @@
 // Copyright © Aptos Foundation
 
-use crate::counters::{test_error, test_fail, test_latency, test_success};
+use crate::{
+    consts::{
+        DEVNET_FAUCET_URL, DEVNET_NODE_URL, FUND_AMOUNT, TESTNET_FAUCET_URL, TESTNET_NODE_URL,
+    },
+    counters::{test_error, test_fail, test_latency, test_step_latency, test_success},
+    fail_message::{ERROR_NO_BALANCE, FAIL_WRONG_BALANCE},
+    tests::{coin_transfer, new_account, nft_transfer, publish_module},
+    time_fn,
+};
 use anyhow::Result;
+use aptos_api_types::U64;
+use aptos_logger::info;
 use aptos_rest_client::{error::RestError, Client, FaucetClient};
 use aptos_sdk::types::LocalAccount;
-use once_cell::sync::Lazy;
+use aptos_types::account_address::AccountAddress;
 use std::env;
-use url::Url;
-
-// network urls
-pub static DEVNET_NODE_URL: Lazy<Url> =
-    Lazy::new(|| Url::parse("https://fullnode.devnet.aptoslabs.com").unwrap());
-pub static DEVNET_FAUCET_URL: Lazy<Url> =
-    Lazy::new(|| Url::parse("https://faucet.devnet.aptoslabs.com").unwrap());
-pub static TESTNET_NODE_URL: Lazy<Url> =
-    Lazy::new(|| Url::parse("https://fullnode.testnet.aptoslabs.com").unwrap());
-pub static TESTNET_FAUCET_URL: Lazy<Url> =
-    Lazy::new(|| Url::parse("https://faucet.testnet.aptoslabs.com").unwrap());
 
-#[derive(Debug)]
-pub enum TestResult {
-    Success,
-    Fail(&'static str),
-    Error(anyhow::Error),
-}
-
-impl From<TestFailure> for TestResult {
-    fn from(f: TestFailure) -> TestResult {
-        match f {
-            TestFailure::Fail(f) => TestResult::Fail(f),
-            TestFailure::Error(e) => TestResult::Error(e),
-        }
-    }
-}
+// test failure
 
 #[derive(Debug)]
 pub enum TestFailure {
@@ -52,6 +37,9 @@ impl From<anyhow::Error> for TestFailure {
     }
 }
 
+// test name
+
+#[derive(Clone, Copy)]
 pub enum TestName {
     NewAccount,
     CoinTransfer,
@@ -59,6 +47,19 @@ pub enum TestName {
     PublishModule,
 }
 
+impl TestName {
+    pub async fn run(&self, network_name: NetworkName, run_id: &str) {
+        let output = match &self {
+            TestName::NewAccount => time_fn!(new_account::test, network_name, run_id),
+            TestName::CoinTransfer => time_fn!(coin_transfer::test, network_name, run_id),
+            TestName::NftTransfer => time_fn!(nft_transfer::test, network_name, run_id),
+            TestName::PublishModule => time_fn!(publish_module::test, network_name, run_id),
+        };
+
+        emit_test_metrics(output, *self, network_name, run_id);
+    }
+}
+
 impl ToString for TestName {
     fn to_string(&self) -> String {
         match &self {
@@ -70,6 +71,8 @@ impl ToString for TestName {
     }
 }
 
+// network name
+
 #[derive(Clone, Copy)]
 pub enum NetworkName {
     Testnet,
@@ -85,37 +88,9 @@ impl ToString for NetworkName {
     }
 }
 
-// Set metrics based on the result.
-pub fn set_metrics(
-    output: &TestResult,
-    test_name: &str,
-    network_name: &str,
-    start_time: &str,
-    time: f64,
-) {
-    match output {
-        TestResult::Success => {
-            test_success(test_name, network_name, start_time).observe(1_f64);
-            test_fail(test_name, network_name, start_time).observe(0_f64);
-            test_error(test_name, network_name, start_time).observe(0_f64);
-            test_latency(test_name, network_name, start_time, "success").observe(time);
-        },
-        TestResult::Fail(_) => {
-            test_success(test_name, network_name, start_time).observe(0_f64);
-            test_fail(test_name, network_name, start_time).observe(1_f64);
-            test_error(test_name, network_name, start_time).observe(0_f64);
-            test_latency(test_name, network_name, start_time, "fail").observe(time);
-        },
-        TestResult::Error(_) => {
-            test_success(test_name, network_name, start_time).observe(0_f64);
-            test_fail(test_name, network_name, start_time).observe(0_f64);
-            test_error(test_name, network_name, start_time).observe(1_f64);
-            test_latency(test_name, network_name, start_time, "error").observe(time);
-        },
-    }
-}
+// setup helpers
 
-// Create a REST client.
+/// Create a REST client.
 pub fn get_client(network_name: NetworkName) -> Client {
     match network_name {
         NetworkName::Testnet => Client::new(TESTNET_NODE_URL.clone()),
@@ -123,7 +98,7 @@ pub fn get_client(network_name: NetworkName) -> Client {
     }
 }
 
-// Create a faucet client.
+/// Create a faucet client.
 pub fn get_faucet_client(network_name: NetworkName) -> FaucetClient {
     match network_name {
         NetworkName::Testnet => {
@@ -140,7 +115,7 @@ pub fn get_faucet_client(network_name: NetworkName) -> FaucetClient {
     }
 }
 
-// Create an account with zero balance.
+/// Create an account with zero balance.
 pub async fn create_account(faucet_client: &FaucetClient) -> Result<LocalAccount> {
     let account = LocalAccount::generate(&mut rand::rngs::OsRng);
     faucet_client.create_account(account.address()).await?;
@@ -148,10 +123,147 @@ pub async fn create_account(faucet_client: &FaucetClient) -> Result<LocalAccount
     Ok(account)
 }
 
-// Create an account with 100_000_000 balance.
+/// Create an account with 100_000_000 balance.
 pub async fn create_and_fund_account(faucet_client: &FaucetClient) -> Result<LocalAccount> {
     let account = LocalAccount::generate(&mut rand::rngs::OsRng);
-    faucet_client.fund(account.address(), 100_000_000).await?;
+    faucet_client.fund(account.address(), FUND_AMOUNT).await?;
 
     Ok(account)
 }
+
+/// Check account balance.
+pub async fn check_balance(
+    test_name: TestName,
+    client: &Client,
+    address: AccountAddress,
+    expected: U64,
+) -> Result<(), TestFailure> {
+    // actual
+    let actual = match client.get_account_balance(address).await {
+        Ok(response) => response.into_inner().coin.value,
+        Err(e) => {
+            info!(
+                "test: {} part: check_account_data ERROR: {}, with error {:?}",
+                &test_name.to_string(),
+                ERROR_NO_BALANCE,
+                e
+            );
+            return Err(e.into());
+        },
+    };
+
+    // compare
+    if expected != actual {
+        info!(
+            "test: {} part: check_account_data FAIL: {}, expected {:?}, got {:?}",
+            &test_name.to_string(),
+            FAIL_WRONG_BALANCE,
+            expected,
+            actual
+        );
+        return Err(TestFailure::Fail(FAIL_WRONG_BALANCE));
+    }
+
+    Ok(())
+}
+
+// metrics helpers
+
+/// Emit metrics based on test result.
+pub fn emit_test_metrics(
+    output: (Result<(), TestFailure>, f64),
+    test_name: TestName,
+    network_name: NetworkName,
+    run_id: &str,
+) {
+    // deconstruct
+    let (result, time) = output;
+
+    // emit success rate and get result word
+    let result_label = match result {
+        Ok(_) => {
+            test_success(&test_name.to_string(), &network_name.to_string(), run_id).observe(1_f64);
+            test_fail(&test_name.to_string(), &network_name.to_string(), run_id).observe(0_f64);
+            test_error(&test_name.to_string(), &network_name.to_string(), run_id).observe(0_f64);
+
+            "success"
+        },
+        Err(e) => match e {
+            TestFailure::Fail(_) => {
+                test_success(&test_name.to_string(), &network_name.to_string(), run_id)
+                    .observe(0_f64);
+                test_fail(&test_name.to_string(), &network_name.to_string(), run_id).observe(1_f64);
+                test_error(&test_name.to_string(), &network_name.to_string(), run_id)
+                    .observe(0_f64);
+
+                "fail"
+            },
+            TestFailure::Error(_) => {
+                test_success(&test_name.to_string(), &network_name.to_string(), run_id)
+                    .observe(0_f64);
+                test_fail(&test_name.to_string(), &network_name.to_string(), run_id).observe(0_f64);
+                test_error(&test_name.to_string(), &network_name.to_string(), run_id)
+                    .observe(1_f64);
+
+                "error"
+            },
+        },
+    };
+
+    // log result
+    info!(
+        "----- TEST FINISHED test: {} result: {} time: {} -----",
+        test_name.to_string(),
+        result_label,
+        time,
+    );
+
+    // emit latency
+    test_latency(
+        &test_name.to_string(),
+        &network_name.to_string(),
+        run_id,
+        result_label,
+    )
+    .observe(time);
+}
+
+/// Emit metrics based on  result.
+pub fn emit_step_metrics<T>(
+    output: (Result<T, TestFailure>, f64),
+    test_name: TestName,
+    step_name: &str,
+    network_name: NetworkName,
+    run_id: &str,
+) -> Result<T, TestFailure> {
+    // deconstruct and get result word
+    let (result, time) = output;
+    let result_label = match &result {
+        Ok(_) => "success",
+        Err(e) => match e {
+            TestFailure::Fail(_) => "fail",
+            TestFailure::Error(_) => "error",
+        },
+    };
+
+    // log result
+    info!(
+        "STEP FINISHED test: {} step: {} result: {} time: {}",
+        test_name.to_string(),
+        step_name,
+        result_label,
+        time,
+    );
+
+    // emit latency
+    test_step_latency(
+        &test_name.to_string(),
+        step_name,
+        &network_name.to_string(),
+        run_id,
+        result_label,
+    )
+    .observe(time);
+
+    result
+}
diff --git a/crates/aptos-rest-client/src/lib.rs b/crates/aptos-rest-client/src/lib.rs
index 1a6f653636f47..43ef9e5d5ce58 100644
--- a/crates/aptos-rest-client/src/lib.rs
+++ b/crates/aptos-rest-client/src/lib.rs
@@ -566,7 +566,8 @@ impl Client {
         F: Fn(HashValue) -> Fut,
         Fut: Future<Output = AptosResult<WaitForTransactionResult<T>>>,
     {
-        const DEFAULT_DELAY: Duration = Duration::from_millis(500);
+        // note: make this configurable
+        const DEFAULT_DELAY: Duration = Duration::from_millis(100);
         let mut reached_mempool = false;
         let start = std::time::Instant::now();
         loop {