Jelajahi Sumber

Merge branch 'main' into pyth-stylus-governance-impl

Ayush Suresh 4 bulan lalu
induk
melakukan
9927f8ae3e

+ 6 - 0
.github/workflows/ci-lazer-rust.yml

@@ -41,6 +41,12 @@ jobs:
       - name: Clippy check
         run: cargo clippy -p pyth-lazer-protocol -p pyth-lazer-client -p pyth-lazer-publisher-sdk --all-targets -- --deny warnings
         if: success() || failure()
+      - name: Clippy check with mry
+        run: cargo clippy -F mry -p pyth-lazer-protocol --all-targets -- --deny warnings
+        if: success() || failure()
       - name: test
         run: cargo test -p pyth-lazer-protocol -p pyth-lazer-client -p pyth-lazer-publisher-sdk
         if: success() || failure()
+      - name: test with mry
+        run: cargo test -F mry -p pyth-lazer-protocol
+        if: success() || failure()

+ 54 - 14
Cargo.lock

@@ -737,6 +737,17 @@ dependencies = [
  "pin-project-lite",
 ]
 
+[[package]]
+name = "async-recursion"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.104",
+]
+
 [[package]]
 name = "async-trait"
 version = "0.1.88"
@@ -1524,8 +1535,10 @@ checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
 dependencies = [
  "android-tzdata",
  "iana-time-zone",
+ "js-sys",
  "num-traits",
  "serde",
+ "wasm-bindgen",
  "windows-link",
 ]
 
@@ -3058,7 +3071,7 @@ dependencies = [
 
 [[package]]
 name = "fortuna"
-version = "8.2.0"
+version = "8.2.1"
 dependencies = [
  "anyhow",
  "axum 0.6.20",
@@ -4613,6 +4626,31 @@ dependencies = [
  "syn 2.0.104",
 ]
 
+[[package]]
+name = "mry"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2049b5892de4f1dafb01e30e42db3b42d9a78a8016bce89659322ba6255c519e"
+dependencies = [
+ "async-recursion",
+ "mry_macros",
+ "parking_lot",
+ "send_wrapper 0.6.0",
+ "serde",
+]
+
+[[package]]
+name = "mry_macros"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7d00273ad77a49702501e11864ccbd018514fcbb6028f54c14fb78a5f08d70a"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.104",
+]
+
 [[package]]
 name = "native-tls"
 version = "0.2.14"
@@ -5591,7 +5629,7 @@ dependencies = [
  "hyper 1.6.0",
  "hyper-util",
  "protobuf",
- "pyth-lazer-protocol 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pyth-lazer-protocol 0.8.1",
  "pyth-lazer-publisher-sdk 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "reqwest 0.12.22",
  "serde",
@@ -5621,7 +5659,7 @@ dependencies = [
  "futures-util",
  "hex",
  "libsecp256k1 0.7.2",
- "pyth-lazer-protocol 0.8.1",
+ "pyth-lazer-protocol 0.9.0",
  "serde",
  "serde_json",
  "tokio",
@@ -5633,18 +5671,14 @@ dependencies = [
 [[package]]
 name = "pyth-lazer-protocol"
 version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1258b8770756a82a39b7b02a296c10a91b93aa58c0cded47950defe4d9377644"
 dependencies = [
- "alloy-primitives 0.8.25",
  "anyhow",
- "bincode 1.3.3",
- "bs58",
  "byteorder",
  "derive_more 1.0.0",
- "ed25519-dalek 2.1.1",
- "hex",
  "humantime-serde",
  "itertools 0.13.0",
- "libsecp256k1 0.7.2",
  "protobuf",
  "rust_decimal",
  "serde",
@@ -5653,15 +5687,21 @@ dependencies = [
 
 [[package]]
 name = "pyth-lazer-protocol"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1258b8770756a82a39b7b02a296c10a91b93aa58c0cded47950defe4d9377644"
+version = "0.9.0"
 dependencies = [
+ "alloy-primitives 0.8.25",
  "anyhow",
+ "bincode 1.3.3",
+ "bs58",
  "byteorder",
+ "chrono",
  "derive_more 1.0.0",
+ "ed25519-dalek 2.1.1",
+ "hex",
  "humantime-serde",
  "itertools 0.13.0",
+ "libsecp256k1 0.7.2",
+ "mry",
  "protobuf",
  "rust_decimal",
  "serde",
@@ -5677,7 +5717,7 @@ dependencies = [
  "humantime",
  "protobuf",
  "protobuf-codegen",
- "pyth-lazer-protocol 0.8.1",
+ "pyth-lazer-protocol 0.9.0",
  "serde-value",
  "tracing",
 ]
@@ -5693,7 +5733,7 @@ dependencies = [
  "humantime",
  "protobuf",
  "protobuf-codegen",
- "pyth-lazer-protocol 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pyth-lazer-protocol 0.8.1",
  "serde-value",
  "tracing",
 ]

+ 1 - 1
apps/entropy-tester/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@pythnetwork/entropy-tester",
-  "version": "1.1.0",
+  "version": "1.2.0",
   "description": "Utility to test entropy provider callbacks",
   "private": true,
   "type": "module",

+ 60 - 24
apps/entropy-tester/src/index.ts

@@ -11,9 +11,12 @@ import yargs from "yargs";
 import { hideBin } from "yargs/helpers";
 import { z } from "zod";
 
+const DEFAULT_RETRIES = 3;
+
 type LoadedConfig = {
   contract: EvmEntropyContract;
   interval: number;
+  retries: number;
 };
 
 function timeToSeconds(timeStr: string): number {
@@ -46,6 +49,7 @@ async function loadConfig(configPath: string): Promise<LoadedConfig[]> {
       "chain-id": z.string(),
       interval: z.string(),
       "rpc-endpoint": z.string().optional(),
+      retries: z.number().default(DEFAULT_RETRIES),
     }),
   );
   const configContent = (await import(configPath, {
@@ -78,7 +82,7 @@ async function loadConfig(configPath: string): Promise<LoadedConfig[]> {
         evmChain.networkId,
       );
     }
-    return { contract: firstContract, interval };
+    return { contract: firstContract, interval, retries: config.retries };
   });
   return loadedConfigs;
 }
@@ -188,31 +192,63 @@ export const main = function () {
           privateKeyFileContent.replace("0x", "").trimEnd(),
         );
         logger.info("Running");
-        const promises = configs.map(async ({ contract, interval }) => {
-          const child = logger.child({ chain: contract.chain.getId() });
-          // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
-          while (true) {
-            try {
-              await Promise.race([
-                testLatency(contract, privateKey, child),
-                new Promise((_, reject) =>
-                  setTimeout(() => {
-                    reject(
-                      new Error(
-                        "Timeout: 120s passed but testLatency function was not resolved",
-                      ),
+        const promises = configs.map(
+          async ({ contract, interval, retries }) => {
+            const child = logger.child({ chain: contract.chain.getId() });
+            // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+            while (true) {
+              let lastError: Error | undefined;
+              let success = false;
+
+              for (let attempt = 1; attempt <= retries; attempt++) {
+                try {
+                  await Promise.race([
+                    testLatency(contract, privateKey, child),
+                    new Promise((_, reject) =>
+                      setTimeout(() => {
+                        reject(
+                          new Error(
+                            "Timeout: 120s passed but testLatency function was not resolved",
+                          ),
+                        );
+                      }, 120_000),
+                    ),
+                  ]);
+                  success = true;
+                  break;
+                } catch (error) {
+                  lastError = error as Error;
+                  child.warn(
+                    { attempt, maxRetries: retries, error: error },
+                    `Attempt ${attempt.toString()}/${retries.toString()} failed, ${attempt < retries ? "retrying..." : "all retries exhausted"}`,
+                  );
+
+                  if (attempt < retries) {
+                    // Wait a bit before retrying (exponential backoff, max 10s)
+                    const backoffDelay = Math.min(
+                      2000 * Math.pow(2, attempt - 1),
+                      10_000,
+                    );
+                    await new Promise((resolve) =>
+                      setTimeout(resolve, backoffDelay),
                     );
-                  }, 120_000),
-                ),
-              ]);
-            } catch (error) {
-              child.error(error, "Error testing latency");
+                  }
+                }
+              }
+
+              if (!success && lastError) {
+                child.error(
+                  { error: lastError, retriesExhausted: retries },
+                  "All retries exhausted, callback was not called.",
+                );
+              }
+
+              await new Promise((resolve) =>
+                setTimeout(resolve, interval * 1000),
+              );
             }
-            await new Promise((resolve) =>
-              setTimeout(resolve, interval * 1000),
-            );
-          }
-        });
+          },
+        );
         await Promise.all(promises);
       },
     )

+ 1 - 1
apps/fortuna/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = "fortuna"
-version = "8.2.0"
+version = "8.2.1"
 edition = "2021"
 
 [lib]

+ 14 - 2
apps/fortuna/src/eth_utils/utils.rs

@@ -8,7 +8,7 @@ use {
     ethers::{
         contract::{ContractCall, ContractError},
         middleware::Middleware,
-        providers::ProviderError,
+        providers::{MiddlewareError, ProviderError},
         signers::Signer,
         types::{
             transaction::eip2718::TypedTransaction, TransactionReceipt, TransactionRequest, U256,
@@ -253,7 +253,19 @@ pub async fn submit_tx<T: Middleware + NonceManaged + 'static>(
     client
         .fill_transaction(&mut transaction, None)
         .await
-        .map_err(|e| backoff::Error::transient(SubmitTxError::GasPriceEstimateError(e)))?;
+        .map_err(|e| {
+            // If there is revert data, the contract reverted during gas usage estimation.
+            if let Some(e) = e.as_error_response() {
+                if let Some(e) = e.as_revert_data() {
+                    return backoff::Error::transient(SubmitTxError::GasUsageEstimateError(
+                        ContractError::Revert(e.clone()),
+                    ));
+                }
+            }
+
+            // If there is no revert data, there was likely an error during gas price polling.
+            backoff::Error::transient(SubmitTxError::GasPriceEstimateError(e))
+        })?;
 
     // Apply the fee escalation policy. Note: the unwrap_or_default should never default as we have a gas oracle
     // in the client that sets the gas price.

+ 12 - 1
apps/fortuna/src/history.rs

@@ -357,7 +357,18 @@ impl History {
             }
         };
         if let Err(e) = result {
-            tracing::error!("Failed to update request status: {}", e);
+            match e.as_database_error() {
+                Some(db_error) if db_error.is_unique_violation() => {
+                    tracing::info!(
+                        "Failed to insert request, request already exists: Chain ID: {}, Sequence: {}",
+                        network_id,
+                        sequence
+                    );
+                }
+                _ => {
+                    tracing::error!("Failed to update request status: {}", e);
+                }
+            }
         }
     }
 

+ 9 - 1
apps/fortuna/src/keeper/fee.rs

@@ -44,6 +44,13 @@ async fn calculate_fair_fee_withdrawal_amount<M: Middleware + 'static>(
         .await
         .map_err(|e| anyhow!("Error while getting current keeper balance. error: {:?}", e))?;
 
+    tracing::info!(
+        "Contract has available fees: {:?}, current keeper ({:?}) has balance: {:?}",
+        available_fees,
+        keeper_address,
+        current_balance
+    );
+
     // Calculate total funds across all keepers + available fees
     let mut total_funds = current_balance + available_fees;
 
@@ -55,6 +62,7 @@ async fn calculate_fair_fee_withdrawal_amount<M: Middleware + 'static>(
                 e
             )
         })?;
+        tracing::info!("Keeper address {:?} has balance: {:?}", address, balance);
         total_funds += balance;
     }
 
@@ -174,7 +182,7 @@ pub async fn withdraw_fees_if_necessary(
     if withdrawal_amount < min_withdrawal_amount {
         // We don't have enough to meaningfully top up the balance.
         // NOTE: This log message triggers a grafana alert. If you want to change the text, please change the alert also.
-        tracing::warn!("Keeper balance {:?} is too low (< {:?}) but provider fees are not sufficient to top-up.", keeper_balance, min_balance);
+        tracing::warn!("Keeper balance {:?} is too low (< {:?}) but provider fees are not sufficient to top-up. (withdrawal_amount={:?} < min_withdrawal_amount={:?})", keeper_balance, min_balance, withdrawal_amount, min_withdrawal_amount);
         return Ok(());
     }
 

+ 2 - 2
governance/pyth_staking_sdk/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@pythnetwork/staking-sdk",
-  "version": "0.2.3",
+  "version": "0.2.4",
   "description": "Pyth staking SDK",
   "type": "module",
   "exports": {
@@ -13,7 +13,7 @@
     "dist/**/*"
   ],
   "engines": {
-    "node": "20 || 22"
+    "node": "20 || 22 || 24"
   },
   "publishConfig": {
     "access": "public"

+ 10 - 3
lazer/contracts/solana/Cargo.lock

@@ -991,9 +991,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
 
 [[package]]
 name = "chrono"
-version = "0.4.38"
+version = "0.4.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
+checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
 dependencies = [
  "android-tzdata",
  "iana-time-zone",
@@ -1001,7 +1001,7 @@ dependencies = [
  "num-traits",
  "serde",
  "wasm-bindgen",
- "windows-targets 0.52.6",
+ "windows-link",
 ]
 
 [[package]]
@@ -3208,6 +3208,7 @@ version = "0.8.1"
 dependencies = [
  "anyhow",
  "byteorder",
+ "chrono",
  "derive_more",
  "humantime-serde",
  "itertools 0.13.0",
@@ -6359,6 +6360,12 @@ dependencies = [
  "windows-targets 0.52.6",
 ]
 
+[[package]]
+name = "windows-link"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
+
 [[package]]
 name = "windows-sys"
 version = "0.48.0"

+ 1 - 1
lazer/contracts/solana/programs/pyth-lazer-solana-contract/Cargo.toml

@@ -22,7 +22,7 @@ no-log-ix-name = []
 idl-build = ["anchor-lang/idl-build"]
 
 [dependencies]
-pyth-lazer-protocol = { path = "../../../../sdk/rust/protocol", version = "0.8.1" }
+pyth-lazer-protocol = { path = "../../../../sdk/rust/protocol", version = "0.9.0" }
 
 anchor-lang = "0.30.1"
 bytemuck = "1.20.0"

+ 1 - 1
lazer/publisher_sdk/rust/Cargo.toml

@@ -7,7 +7,7 @@ license = "Apache-2.0"
 repository = "https://github.com/pyth-network/pyth-crosschain"
 
 [dependencies]
-pyth-lazer-protocol = { version = "0.8.1", path = "../../sdk/rust/protocol" }
+pyth-lazer-protocol = { version = "0.9.0", path = "../../sdk/rust/protocol" }
 anyhow = "1.0.98"
 protobuf = "3.7.2"
 serde-value = "0.7.0"

+ 2 - 2
lazer/publisher_sdk/rust/src/lib.rs

@@ -7,7 +7,7 @@ use anyhow::{bail, ensure, Context};
 use humantime::format_duration;
 use protobuf::dynamic_value::{dynamic_value, DynamicValue};
 use pyth_lazer_protocol::jrpc::{FeedUpdateParams, UpdateParams};
-use pyth_lazer_protocol::router::TimestampUs;
+use pyth_lazer_protocol::time::TimestampUs;
 
 pub mod transaction_envelope {
     pub use crate::protobuf::transaction_envelope::*;
@@ -141,7 +141,7 @@ impl TryFrom<DynamicValue> for serde_value::Value {
             }
             dynamic_value::Value::TimestampValue(ts) => {
                 let ts = TimestampUs::try_from(&ts)?;
-                Ok(serde_value::Value::U64(ts.0))
+                Ok(serde_value::Value::U64(ts.as_micros()))
             }
             dynamic_value::Value::List(list) => {
                 let mut output = Vec::new();

+ 1 - 1
lazer/sdk/rust/client/Cargo.toml

@@ -6,7 +6,7 @@ description = "A Rust client for Pyth Lazer"
 license = "Apache-2.0"
 
 [dependencies]
-pyth-lazer-protocol = { path = "../protocol", version = "0.8.1" }
+pyth-lazer-protocol = { path = "../protocol", version = "0.9.0" }
 tokio = { version = "1", features = ["full"] }
 tokio-tungstenite = { version = "0.20", features = ["native-tls"] }
 futures-util = "0.3"

+ 2 - 6
lazer/sdk/rust/client/examples/subscribe_price_feeds.rs

@@ -44,9 +44,7 @@ async fn main() -> anyhow::Result<()> {
                 delivery_format: DeliveryFormat::Json,
                 json_binary_encoding: JsonBinaryEncoding::Base64,
                 parsed: true,
-                channel: Channel::FixedRate(
-                    FixedRate::from_ms(200).expect("unsupported update rate"),
-                ),
+                channel: Channel::FixedRate(FixedRate::RATE_200_MS),
                 ignore_invalid_feed_ids: false,
             })
             .expect("invalid subscription params"),
@@ -66,9 +64,7 @@ async fn main() -> anyhow::Result<()> {
                 delivery_format: DeliveryFormat::Binary,
                 json_binary_encoding: JsonBinaryEncoding::Base64,
                 parsed: false,
-                channel: Channel::FixedRate(
-                    FixedRate::from_ms(50).expect("unsupported update rate"),
-                ),
+                channel: Channel::FixedRate(FixedRate::RATE_50_MS),
                 ignore_invalid_feed_ids: false,
             })
             .expect("invalid subscription params"),

+ 3 - 1
lazer/sdk/rust/protocol/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = "pyth-lazer-protocol"
-version = "0.8.1"
+version = "0.9.0"
 edition = "2021"
 description = "Pyth Lazer SDK - protocol types."
 license = "Apache-2.0"
@@ -16,6 +16,8 @@ itertools = "0.13.0"
 rust_decimal = "1.36.0"
 protobuf = "3.7.2"
 humantime-serde = "1.1.1"
+mry = { version = "0.13.0", features = ["serde"], optional = true }
+chrono = "0.4.41"
 
 [dev-dependencies]
 bincode = "1.3.3"

+ 3 - 2
lazer/sdk/rust/protocol/src/api.rs

@@ -1,7 +1,8 @@
 use serde::{Deserialize, Serialize};
 
-use crate::router::{
-    Channel, Format, JsonBinaryEncoding, JsonUpdate, PriceFeedId, PriceFeedProperty, TimestampUs,
+use crate::{
+    router::{Channel, Format, JsonBinaryEncoding, JsonUpdate, PriceFeedId, PriceFeedProperty},
+    time::TimestampUs,
 };
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]

+ 6 - 5
lazer/sdk/rust/protocol/src/jrpc.rs

@@ -1,5 +1,6 @@
-use crate::router::{Channel, Price, PriceFeedId, Rate, TimestampUs};
+use crate::router::{Channel, Price, PriceFeedId, Rate};
 use crate::symbol_state::SymbolState;
+use crate::time::TimestampUs;
 use serde::{Deserialize, Serialize};
 use std::time::Duration;
 
@@ -157,7 +158,7 @@ mod tests {
             jsonrpc: JsonRpcVersion::V2,
             params: PushUpdate(FeedUpdateParams {
                 feed_id: PriceFeedId(1),
-                source_timestamp: TimestampUs(124214124124),
+                source_timestamp: TimestampUs::from_micros(124214124124),
                 update: UpdateParams::PriceUpdate {
                     price: Price::from_integer(1234567890, 0).unwrap(),
                     best_bid_price: Some(Price::from_integer(1234567891, 0).unwrap()),
@@ -196,7 +197,7 @@ mod tests {
             jsonrpc: JsonRpcVersion::V2,
             params: PushUpdate(FeedUpdateParams {
                 feed_id: PriceFeedId(1),
-                source_timestamp: TimestampUs(124214124124),
+                source_timestamp: TimestampUs::from_micros(124214124124),
                 update: UpdateParams::PriceUpdate {
                     price: Price::from_integer(1234567890, 0).unwrap(),
                     best_bid_price: None,
@@ -236,7 +237,7 @@ mod tests {
             jsonrpc: JsonRpcVersion::V2,
             params: PushUpdate(FeedUpdateParams {
                 feed_id: PriceFeedId(1),
-                source_timestamp: TimestampUs(124214124124),
+                source_timestamp: TimestampUs::from_micros(124214124124),
                 update: UpdateParams::FundingRateUpdate {
                     price: Some(Price::from_integer(1234567890, 0).unwrap()),
                     rate: Rate::from_integer(1234567891, 0).unwrap(),
@@ -273,7 +274,7 @@ mod tests {
             jsonrpc: JsonRpcVersion::V2,
             params: PushUpdate(FeedUpdateParams {
                 feed_id: PriceFeedId(1),
-                source_timestamp: TimestampUs(124214124124),
+                source_timestamp: TimestampUs::from_micros(124214124124),
                 update: UpdateParams::FundingRateUpdate {
                     price: None,
                     rate: Rate::from_integer(1234567891, 0).unwrap(),

+ 1 - 0
lazer/sdk/rust/protocol/src/lib.rs

@@ -11,6 +11,7 @@ mod serde_price_as_i64;
 mod serde_str;
 pub mod subscription;
 pub mod symbol_state;
+pub mod time;
 
 #[test]
 fn magics_in_big_endian() {

+ 9 - 6
lazer/sdk/rust/protocol/src/payload.rs

@@ -1,8 +1,11 @@
 //! Types representing binary encoding of signable payloads and signature envelopes.
 
 use {
-    super::router::{PriceFeedId, PriceFeedProperty, TimestampUs},
-    crate::router::{ChannelId, Price, Rate},
+    super::router::{PriceFeedId, PriceFeedProperty},
+    crate::{
+        router::{ChannelId, Price, Rate},
+        time::TimestampUs,
+    },
     anyhow::bail,
     byteorder::{ByteOrder, ReadBytesExt, WriteBytesExt, BE, LE},
     serde::{Deserialize, Serialize},
@@ -103,7 +106,7 @@ impl PayloadData {
 
     pub fn serialize<BO: ByteOrder>(&self, mut writer: impl Write) -> anyhow::Result<()> {
         writer.write_u32::<BO>(PAYLOAD_FORMAT_MAGIC)?;
-        writer.write_u64::<BO>(self.timestamp_us.0)?;
+        writer.write_u64::<BO>(self.timestamp_us.as_micros())?;
         writer.write_u8(self.channel_id.0)?;
         writer.write_u8(self.feeds.len().try_into()?)?;
         for feed in &self.feeds {
@@ -162,7 +165,7 @@ impl PayloadData {
         if magic != PAYLOAD_FORMAT_MAGIC {
             bail!("magic mismatch");
         }
-        let timestamp_us = TimestampUs(reader.read_u64::<BO>()?);
+        let timestamp_us = TimestampUs::from_micros(reader.read_u64::<BO>()?);
         let channel_id = ChannelId(reader.read_u8()?);
         let num_feeds = reader.read_u8()?;
         let mut feeds = Vec::with_capacity(num_feeds.into());
@@ -252,7 +255,7 @@ fn write_option_timestamp<BO: ByteOrder>(
     match value {
         Some(value) => {
             writer.write_u8(1)?;
-            writer.write_u64::<BO>(value.0)
+            writer.write_u64::<BO>(value.as_micros())
         }
         None => {
             writer.write_u8(0)?;
@@ -266,7 +269,7 @@ fn read_option_timestamp<BO: ByteOrder>(
 ) -> std::io::Result<Option<TimestampUs>> {
     let present = reader.read_u8()? != 0;
     if present {
-        Ok(Some(TimestampUs(reader.read_u64::<BO>()?)))
+        Ok(Some(TimestampUs::from_micros(reader.read_u64::<BO>()?)))
     } else {
         Ok(None)
     }

+ 10 - 9
lazer/sdk/rust/protocol/src/publisher.rs

@@ -3,7 +3,8 @@
 //! eliminating WebSocket overhead.
 
 use {
-    super::router::{Price, PriceFeedId, Rate, TimestampUs},
+    super::router::{Price, PriceFeedId, Rate},
+    crate::time::TimestampUs,
     derive_more::derive::From,
     serde::{Deserialize, Serialize},
 };
@@ -101,8 +102,8 @@ fn price_feed_data_v1_serde() {
 
     let expected = PriceFeedDataV1 {
         price_feed_id: PriceFeedId(1),
-        source_timestamp_us: TimestampUs(2),
-        publisher_timestamp_us: TimestampUs(3),
+        source_timestamp_us: TimestampUs::from_micros(2),
+        publisher_timestamp_us: TimestampUs::from_micros(3),
         price: Some(Price(4.try_into().unwrap())),
         best_bid_price: Some(Price(5.try_into().unwrap())),
         best_ask_price: Some(Price((2 * 256 + 6).try_into().unwrap())),
@@ -123,8 +124,8 @@ fn price_feed_data_v1_serde() {
     ];
     let expected2 = PriceFeedDataV1 {
         price_feed_id: PriceFeedId(1),
-        source_timestamp_us: TimestampUs(2),
-        publisher_timestamp_us: TimestampUs(3),
+        source_timestamp_us: TimestampUs::from_micros(2),
+        publisher_timestamp_us: TimestampUs::from_micros(3),
         price: Some(Price(4.try_into().unwrap())),
         best_bid_price: None,
         best_ask_price: None,
@@ -150,8 +151,8 @@ fn price_feed_data_v2_serde() {
 
     let expected = PriceFeedDataV2 {
         price_feed_id: PriceFeedId(1),
-        source_timestamp_us: TimestampUs(2),
-        publisher_timestamp_us: TimestampUs(3),
+        source_timestamp_us: TimestampUs::from_micros(2),
+        publisher_timestamp_us: TimestampUs::from_micros(3),
         price: Some(Price(4.try_into().unwrap())),
         best_bid_price: Some(Price(5.try_into().unwrap())),
         best_ask_price: Some(Price((2 * 256 + 6).try_into().unwrap())),
@@ -174,8 +175,8 @@ fn price_feed_data_v2_serde() {
     ];
     let expected2 = PriceFeedDataV2 {
         price_feed_id: PriceFeedId(1),
-        source_timestamp_us: TimestampUs(2),
-        publisher_timestamp_us: TimestampUs(3),
+        source_timestamp_us: TimestampUs::from_micros(2),
+        publisher_timestamp_us: TimestampUs::from_micros(3),
         price: Some(Price(4.try_into().unwrap())),
         best_bid_price: None,
         best_ask_price: None,

+ 72 - 66
lazer/sdk/rust/protocol/src/router.rs

@@ -1,18 +1,20 @@
 //! WebSocket JSON protocol types for the API the router provides to consumers and publishers.
 
-use protobuf::MessageField;
 use {
-    crate::payload::AggregatedPriceFeedData,
+    crate::{
+        payload::AggregatedPriceFeedData,
+        time::{DurationUs, TimestampUs},
+    },
     anyhow::{bail, Context},
+    derive_more::derive::From,
     itertools::Itertools,
-    protobuf::well_known_types::timestamp::Timestamp,
+    protobuf::well_known_types::duration::Duration as ProtobufDuration,
     rust_decimal::{prelude::FromPrimitive, Decimal},
     serde::{de::Error, Deserialize, Serialize},
     std::{
         fmt::Display,
         num::NonZeroI64,
         ops::{Add, Deref, DerefMut, Div, Sub},
-        time::{SystemTime, UNIX_EPOCH},
     },
 };
 
@@ -25,53 +27,6 @@ pub struct PriceFeedId(pub u32);
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
 pub struct ChannelId(pub u8);
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
-pub struct TimestampUs(pub u64);
-
-impl TryFrom<&Timestamp> for TimestampUs {
-    type Error = anyhow::Error;
-
-    fn try_from(timestamp: &Timestamp) -> anyhow::Result<Self> {
-        let seconds_in_micros: u64 = (timestamp.seconds * 1_000_000).try_into()?;
-        let nanos_in_micros: u64 = (timestamp.nanos / 1_000).try_into()?;
-        Ok(TimestampUs(seconds_in_micros + nanos_in_micros))
-    }
-}
-
-impl From<TimestampUs> for Timestamp {
-    fn from(value: TimestampUs) -> Self {
-        Timestamp {
-            // u64 to i64 after this division can never overflow because the value cannot be too big
-            #[allow(clippy::cast_possible_wrap)]
-            seconds: (value.0 / 1_000_000) as i64,
-            nanos: (value.0 % 1_000_000) as i32 * 1000,
-            special_fields: Default::default(),
-        }
-    }
-}
-
-impl From<TimestampUs> for MessageField<Timestamp> {
-    fn from(value: TimestampUs) -> Self {
-        MessageField::some(value.into())
-    }
-}
-
-impl TimestampUs {
-    pub fn now() -> Self {
-        let value = SystemTime::now()
-            .duration_since(UNIX_EPOCH)
-            .expect("invalid system time")
-            .as_micros()
-            .try_into()
-            .expect("invalid system time");
-        Self(value)
-    }
-
-    pub fn saturating_us_since(self, other: Self) -> u64 {
-        self.0.saturating_sub(other.0)
-    }
-}
-
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
 #[repr(transparent)]
 pub struct Rate(pub i64);
@@ -244,7 +199,7 @@ pub enum JsonBinaryEncoding {
     Hex,
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, From)]
 pub enum Channel {
     FixedRate(FixedRate),
 }
@@ -259,7 +214,10 @@ impl Serialize for Channel {
                 if *fixed_rate == FixedRate::MIN {
                     return serializer.serialize_str("real_time");
                 }
-                serializer.serialize_str(&format!("fixed_rate@{}ms", fixed_rate.value_ms()))
+                serializer.serialize_str(&format!(
+                    "fixed_rate@{}ms",
+                    fixed_rate.duration().as_millis()
+                ))
             }
         }
     }
@@ -278,7 +236,7 @@ impl Display for Channel {
         match self {
             Channel::FixedRate(fixed_rate) => match *fixed_rate {
                 FixedRate::MIN => write!(f, "real_time"),
-                rate => write!(f, "fixed_rate@{}ms", rate.value_ms()),
+                rate => write!(f, "fixed_rate@{}ms", rate.duration().as_millis()),
             },
         }
     }
@@ -287,7 +245,7 @@ impl Display for Channel {
 impl Channel {
     pub fn id(&self) -> ChannelId {
         match self {
-            Channel::FixedRate(fixed_rate) => match fixed_rate.value_ms() {
+            Channel::FixedRate(fixed_rate) => match fixed_rate.duration().as_millis() {
                 1 => channel_ids::FIXED_RATE_1,
                 50 => channel_ids::FIXED_RATE_50,
                 200 => channel_ids::FIXED_RATE_200,
@@ -309,7 +267,7 @@ fn parse_channel(value: &str) -> Option<Channel> {
         Some(Channel::FixedRate(FixedRate::MIN))
     } else if let Some(rest) = value.strip_prefix("fixed_rate@") {
         let ms_value = rest.strip_suffix("ms")?;
-        Some(Channel::FixedRate(FixedRate::from_ms(
+        Some(Channel::FixedRate(FixedRate::from_millis(
             ms_value.parse().ok()?,
         )?))
     } else {
@@ -329,27 +287,75 @@ impl<'de> Deserialize<'de> for Channel {
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub struct FixedRate {
-    ms: u32,
+    rate: DurationUs,
 }
 
 impl FixedRate {
+    pub const RATE_1_MS: Self = Self {
+        rate: DurationUs::from_millis_u32(1),
+    };
+    pub const RATE_50_MS: Self = Self {
+        rate: DurationUs::from_millis_u32(50),
+    };
+    pub const RATE_200_MS: Self = Self {
+        rate: DurationUs::from_millis_u32(200),
+    };
+
     // Assumptions (tested below):
     // - Values are sorted.
     // - 1 second contains a whole number of each interval.
     // - all intervals are divisable by the smallest interval.
-    pub const ALL: [Self; 3] = [Self { ms: 1 }, Self { ms: 50 }, Self { ms: 200 }];
+    pub const ALL: [Self; 3] = [Self::RATE_1_MS, Self::RATE_50_MS, Self::RATE_200_MS];
     pub const MIN: Self = Self::ALL[0];
 
-    pub fn from_ms(value: u32) -> Option<Self> {
-        Self::ALL.into_iter().find(|v| v.ms == value)
+    pub fn from_millis(millis: u32) -> Option<Self> {
+        Self::ALL
+            .into_iter()
+            .find(|v| v.rate.as_millis() == u64::from(millis))
+    }
+
+    pub fn duration(self) -> DurationUs {
+        self.rate
+    }
+}
+
+impl TryFrom<DurationUs> for FixedRate {
+    type Error = anyhow::Error;
+
+    fn try_from(value: DurationUs) -> Result<Self, Self::Error> {
+        Self::ALL
+            .into_iter()
+            .find(|v| v.rate == value)
+            .with_context(|| format!("unsupported rate: {value:?}"))
+    }
+}
+
+impl TryFrom<&ProtobufDuration> for FixedRate {
+    type Error = anyhow::Error;
+
+    fn try_from(value: &ProtobufDuration) -> Result<Self, Self::Error> {
+        let duration = DurationUs::try_from(value)?;
+        Self::try_from(duration)
+    }
+}
+
+impl TryFrom<ProtobufDuration> for FixedRate {
+    type Error = anyhow::Error;
+
+    fn try_from(duration: ProtobufDuration) -> anyhow::Result<Self> {
+        TryFrom::<&ProtobufDuration>::try_from(&duration)
     }
+}
 
-    pub fn value_ms(self) -> u32 {
-        self.ms
+impl From<FixedRate> for DurationUs {
+    fn from(value: FixedRate) -> Self {
+        value.rate
     }
+}
 
-    pub fn value_us(self) -> u64 {
-        (self.ms * 1000).into()
+impl From<FixedRate> for ProtobufDuration {
+    fn from(value: FixedRate) -> Self {
+        value.rate.into()
     }
 }
 
@@ -361,12 +367,12 @@ fn fixed_rate_values() {
     );
     for value in FixedRate::ALL {
         assert_eq!(
-            1000 % value.ms,
+            1_000_000 % value.duration().as_micros(),
             0,
             "1 s must contain whole number of intervals"
         );
         assert_eq!(
-            value.value_us() % FixedRate::MIN.value_us(),
+            value.duration().as_micros() % FixedRate::MIN.duration().as_micros(),
             0,
             "the interval's borders must be a subset of the minimal interval's borders"
         );

+ 4 - 4
lazer/sdk/rust/protocol/src/serde_str.rs

@@ -31,7 +31,7 @@ pub mod option_price {
 
 pub mod timestamp {
     use {
-        crate::router::TimestampUs,
+        crate::time::TimestampUs,
         serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer},
     };
 
@@ -39,15 +39,15 @@ pub mod timestamp {
     where
         S: Serializer,
     {
-        value.0.to_string().serialize(serializer)
+        value.as_micros().to_string().serialize(serializer)
     }
 
     pub fn deserialize<'de, D>(deserializer: D) -> Result<TimestampUs, D::Error>
     where
         D: Deserializer<'de>,
     {
-        let value = <&str>::deserialize(deserializer)?;
+        let value = String::deserialize(deserializer)?;
         let value: u64 = value.parse().map_err(D::Error::custom)?;
-        Ok(TimestampUs(value))
+        Ok(TimestampUs::from_micros(value))
     }
 }

+ 488 - 0
lazer/sdk/rust/protocol/src/time.rs

@@ -0,0 +1,488 @@
+#[cfg(test)]
+mod tests;
+
+use {
+    anyhow::Context,
+    protobuf::{
+        well_known_types::{
+            duration::Duration as ProtobufDuration, timestamp::Timestamp as ProtobufTimestamp,
+        },
+        MessageField,
+    },
+    serde::{Deserialize, Serialize},
+    std::time::{Duration, SystemTime},
+};
+
+/// Unix timestamp with microsecond resolution.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
+#[repr(transparent)]
+pub struct TimestampUs(u64);
+
+#[cfg_attr(feature = "mry", mry::mry)]
+impl TimestampUs {
+    pub fn now() -> Self {
+        SystemTime::now().try_into().expect("invalid system time")
+    }
+}
+
+impl TimestampUs {
+    pub const UNIX_EPOCH: Self = Self(0);
+    pub const MAX: Self = Self(u64::MAX);
+
+    #[inline]
+    pub const fn from_micros(micros: u64) -> Self {
+        Self(micros)
+    }
+
+    #[inline]
+    pub const fn as_micros(self) -> u64 {
+        self.0
+    }
+
+    #[inline]
+    pub fn as_nanos(self) -> u128 {
+        // never overflows
+        u128::from(self.0) * 1000
+    }
+
+    #[inline]
+    pub fn as_nanos_i128(self) -> i128 {
+        // never overflows
+        i128::from(self.0) * 1000
+    }
+
+    #[inline]
+    pub fn from_nanos(nanos: u128) -> anyhow::Result<Self> {
+        let micros = nanos
+            .checked_div(1000)
+            .context("nanos.checked_div(1000) failed")?;
+        Ok(Self::from_micros(micros.try_into()?))
+    }
+
+    #[inline]
+    pub fn from_nanos_i128(nanos: i128) -> anyhow::Result<Self> {
+        let micros = nanos
+            .checked_div(1000)
+            .context("nanos.checked_div(1000) failed")?;
+        Ok(Self::from_micros(micros.try_into()?))
+    }
+
+    #[inline]
+    pub fn as_millis(self) -> u64 {
+        self.0 / 1000
+    }
+
+    #[inline]
+    pub fn from_millis(millis: u64) -> anyhow::Result<Self> {
+        let micros = millis
+            .checked_mul(1000)
+            .context("millis.checked_mul(1000) failed")?;
+        Ok(Self::from_micros(micros))
+    }
+
+    #[inline]
+    pub fn as_secs(self) -> u64 {
+        self.0 / 1_000_000
+    }
+
+    #[inline]
+    pub fn from_secs(secs: u64) -> anyhow::Result<Self> {
+        let micros = secs
+            .checked_mul(1_000_000)
+            .context("secs.checked_mul(1_000_000) failed")?;
+        Ok(Self::from_micros(micros))
+    }
+
+    #[inline]
+    pub fn duration_since(self, other: Self) -> anyhow::Result<DurationUs> {
+        Ok(DurationUs(
+            self.0
+                .checked_sub(other.0)
+                .context("timestamp.checked_sub(duration) failed")?,
+        ))
+    }
+
+    #[inline]
+    pub fn saturating_duration_since(self, other: Self) -> DurationUs {
+        DurationUs(self.0.saturating_sub(other.0))
+    }
+
+    #[inline]
+    pub fn elapsed(self) -> anyhow::Result<DurationUs> {
+        Self::now().duration_since(self)
+    }
+
+    #[inline]
+    pub fn saturating_elapsed(self) -> DurationUs {
+        Self::now().saturating_duration_since(self)
+    }
+
+    #[inline]
+    pub fn saturating_add(self, duration: DurationUs) -> TimestampUs {
+        TimestampUs(self.0.saturating_add(duration.0))
+    }
+
+    #[inline]
+    pub fn saturating_sub(self, duration: DurationUs) -> TimestampUs {
+        TimestampUs(self.0.saturating_sub(duration.0))
+    }
+
+    #[inline]
+    pub fn is_multiple_of(self, duration: DurationUs) -> bool {
+        match self.0.checked_rem(duration.0) {
+            Some(rem) => rem == 0,
+            None => true,
+        }
+    }
+
+    /// Calculates the smallest value greater than or equal to self that is a multiple of `duration`.
+    #[inline]
+    pub fn next_multiple_of(self, duration: DurationUs) -> anyhow::Result<TimestampUs> {
+        Ok(TimestampUs(
+            self.0
+                .checked_next_multiple_of(duration.0)
+                .context("checked_next_multiple_of failed")?,
+        ))
+    }
+
+    /// Calculates the smallest value less than or equal to self that is a multiple of `duration`.
+    #[inline]
+    pub fn previous_multiple_of(self, duration: DurationUs) -> anyhow::Result<TimestampUs> {
+        Ok(TimestampUs(
+            self.0
+                .checked_div(duration.0)
+                .context("checked_div failed")?
+                .checked_mul(duration.0)
+                .context("checked_mul failed")?,
+        ))
+    }
+
+    #[inline]
+    pub fn checked_add(self, duration: DurationUs) -> anyhow::Result<Self> {
+        Ok(TimestampUs(
+            self.0
+                .checked_add(duration.0)
+                .context("checked_add failed")?,
+        ))
+    }
+
+    #[inline]
+    pub fn checked_sub(self, duration: DurationUs) -> anyhow::Result<Self> {
+        Ok(TimestampUs(
+            self.0
+                .checked_sub(duration.0)
+                .context("checked_sub failed")?,
+        ))
+    }
+}
+
+impl TryFrom<ProtobufTimestamp> for TimestampUs {
+    type Error = anyhow::Error;
+
+    #[inline]
+    fn try_from(timestamp: ProtobufTimestamp) -> anyhow::Result<Self> {
+        TryFrom::<&ProtobufTimestamp>::try_from(&timestamp)
+    }
+}
+
+impl TryFrom<&ProtobufTimestamp> for TimestampUs {
+    type Error = anyhow::Error;
+
+    fn try_from(timestamp: &ProtobufTimestamp) -> anyhow::Result<Self> {
+        let seconds_in_micros: u64 = timestamp
+            .seconds
+            .checked_mul(1_000_000)
+            .context("checked_mul failed")?
+            .try_into()?;
+        let nanos_in_micros: u64 = timestamp
+            .nanos
+            .checked_div(1_000)
+            .context("checked_div failed")?
+            .try_into()?;
+        Ok(TimestampUs(
+            seconds_in_micros
+                .checked_add(nanos_in_micros)
+                .context("checked_add failed")?,
+        ))
+    }
+}
+
+impl From<TimestampUs> for ProtobufTimestamp {
+    fn from(timestamp: TimestampUs) -> Self {
+        // u64 to i64 after this division can never overflow because the value cannot be too big
+        ProtobufTimestamp {
+            #[allow(clippy::cast_possible_wrap)]
+            seconds: (timestamp.0 / 1_000_000) as i64,
+            // never fails, never overflows
+            nanos: (timestamp.0 % 1_000_000) as i32 * 1000,
+            special_fields: Default::default(),
+        }
+    }
+}
+
+impl From<TimestampUs> for MessageField<ProtobufTimestamp> {
+    #[inline]
+    fn from(value: TimestampUs) -> Self {
+        MessageField::some(value.into())
+    }
+}
+
+impl TryFrom<SystemTime> for TimestampUs {
+    type Error = anyhow::Error;
+
+    fn try_from(value: SystemTime) -> Result<Self, Self::Error> {
+        let value = value
+            .duration_since(SystemTime::UNIX_EPOCH)
+            .context("invalid system time")?
+            .as_micros()
+            .try_into()?;
+        Ok(Self(value))
+    }
+}
+
+impl TryFrom<TimestampUs> for SystemTime {
+    type Error = anyhow::Error;
+
+    fn try_from(value: TimestampUs) -> Result<Self, Self::Error> {
+        SystemTime::UNIX_EPOCH
+            .checked_add(Duration::from_micros(value.as_micros()))
+            .context("checked_add failed")
+    }
+}
+
+impl TryFrom<&chrono::DateTime<chrono::Utc>> for TimestampUs {
+    type Error = anyhow::Error;
+
+    #[inline]
+    fn try_from(value: &chrono::DateTime<chrono::Utc>) -> Result<Self, Self::Error> {
+        Ok(Self(value.timestamp_micros().try_into()?))
+    }
+}
+
+impl TryFrom<chrono::DateTime<chrono::Utc>> for TimestampUs {
+    type Error = anyhow::Error;
+
+    #[inline]
+    fn try_from(value: chrono::DateTime<chrono::Utc>) -> Result<Self, Self::Error> {
+        TryFrom::<&chrono::DateTime<chrono::Utc>>::try_from(&value)
+    }
+}
+
+impl TryFrom<TimestampUs> for chrono::DateTime<chrono::Utc> {
+    type Error = anyhow::Error;
+
+    #[inline]
+    fn try_from(value: TimestampUs) -> Result<Self, Self::Error> {
+        chrono::DateTime::<chrono::Utc>::from_timestamp_micros(value.as_micros().try_into()?)
+            .with_context(|| format!("cannot convert timestamp to datetime: {value:?}"))
+    }
+}
+
+/// Non-negative duration with microsecond resolution.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
+pub struct DurationUs(u64);
+
+impl DurationUs {
+    pub const ZERO: Self = Self(0);
+
+    #[inline]
+    pub const fn from_micros(micros: u64) -> Self {
+        Self(micros)
+    }
+
+    #[inline]
+    pub const fn as_micros(self) -> u64 {
+        self.0
+    }
+
+    #[inline]
+    pub fn as_nanos(self) -> u128 {
+        // never overflows
+        u128::from(self.0) * 1000
+    }
+
+    #[inline]
+    pub fn as_nanos_i128(self) -> i128 {
+        // never overflows
+        i128::from(self.0) * 1000
+    }
+
+    #[inline]
+    pub fn from_nanos(nanos: u128) -> anyhow::Result<Self> {
+        let micros = nanos.checked_div(1000).context("checked_div failed")?;
+        Ok(Self::from_micros(micros.try_into()?))
+    }
+
+    #[inline]
+    pub fn as_millis(self) -> u64 {
+        self.0 / 1000
+    }
+
+    #[inline]
+    pub const fn from_millis_u32(millis: u32) -> Self {
+        // never overflows
+        Self((millis as u64) * 1_000)
+    }
+
+    #[inline]
+    pub fn from_millis(millis: u64) -> anyhow::Result<Self> {
+        let micros = millis
+            .checked_mul(1000)
+            .context("millis.checked_mul(1000) failed")?;
+        Ok(Self::from_micros(micros))
+    }
+
+    #[inline]
+    pub fn as_secs(self) -> u64 {
+        self.0 / 1_000_000
+    }
+
+    #[inline]
+    pub const fn from_secs_u32(secs: u32) -> Self {
+        // never overflows
+        Self((secs as u64) * 1_000_000)
+    }
+
+    #[inline]
+    pub fn from_secs(secs: u64) -> anyhow::Result<Self> {
+        let micros = secs
+            .checked_mul(1_000_000)
+            .context("secs.checked_mul(1_000_000) failed")?;
+        Ok(Self::from_micros(micros))
+    }
+
+    #[inline]
+    pub fn from_days_u16(days: u16) -> Self {
+        // never overflows
+        Self((days as u64) * 24 * 3600 * 1_000_000)
+    }
+
+    #[inline]
+    pub fn is_multiple_of(self, other: DurationUs) -> bool {
+        match self.0.checked_rem(other.0) {
+            Some(rem) => rem == 0,
+            None => true,
+        }
+    }
+
+    #[inline]
+    pub const fn is_zero(self) -> bool {
+        self.0 == 0
+    }
+
+    #[inline]
+    pub const fn is_positive(self) -> bool {
+        self.0 > 0
+    }
+
+    #[inline]
+    pub fn checked_add(self, other: DurationUs) -> anyhow::Result<Self> {
+        Ok(DurationUs(
+            self.0.checked_add(other.0).context("checked_add failed")?,
+        ))
+    }
+
+    #[inline]
+    pub fn checked_sub(self, other: DurationUs) -> anyhow::Result<Self> {
+        Ok(DurationUs(
+            self.0.checked_sub(other.0).context("checked_sub failed")?,
+        ))
+    }
+
+    #[inline]
+    pub fn checked_mul(self, n: u64) -> anyhow::Result<DurationUs> {
+        Ok(DurationUs(
+            self.0.checked_mul(n).context("checked_mul failed")?,
+        ))
+    }
+
+    #[inline]
+    pub fn checked_div(self, n: u64) -> anyhow::Result<DurationUs> {
+        Ok(DurationUs(
+            self.0.checked_div(n).context("checked_div failed")?,
+        ))
+    }
+}
+
+impl From<DurationUs> for Duration {
+    #[inline]
+    fn from(value: DurationUs) -> Self {
+        Duration::from_micros(value.as_micros())
+    }
+}
+
+impl TryFrom<Duration> for DurationUs {
+    type Error = anyhow::Error;
+
+    #[inline]
+    fn try_from(value: Duration) -> Result<Self, Self::Error> {
+        Ok(Self(value.as_micros().try_into()?))
+    }
+}
+
+impl TryFrom<ProtobufDuration> for DurationUs {
+    type Error = anyhow::Error;
+
+    #[inline]
+    fn try_from(duration: ProtobufDuration) -> anyhow::Result<Self> {
+        TryFrom::<&ProtobufDuration>::try_from(&duration)
+    }
+}
+
+impl TryFrom<&ProtobufDuration> for DurationUs {
+    type Error = anyhow::Error;
+
+    fn try_from(duration: &ProtobufDuration) -> anyhow::Result<Self> {
+        let seconds_in_micros: u64 = duration
+            .seconds
+            .checked_mul(1_000_000)
+            .context("checked_mul failed")?
+            .try_into()?;
+        let nanos_in_micros: u64 = duration
+            .nanos
+            .checked_div(1_000)
+            .context("nanos.checked_div(1_000) failed")?
+            .try_into()?;
+        Ok(DurationUs(
+            seconds_in_micros
+                .checked_add(nanos_in_micros)
+                .context("checked_add failed")?,
+        ))
+    }
+}
+
+impl From<DurationUs> for ProtobufDuration {
+    fn from(duration: DurationUs) -> Self {
+        ProtobufDuration {
+            // u64 to i64 after this division can never overflow because the value cannot be too big
+            #[allow(clippy::cast_possible_wrap)]
+            seconds: (duration.0 / 1_000_000) as i64,
+            // never fails, never overflows
+            nanos: (duration.0 % 1_000_000) as i32 * 1000,
+            special_fields: Default::default(),
+        }
+    }
+}
+
+pub mod duration_us_serde_humantime {
+    use std::time::Duration;
+
+    use serde::{de::Error, Deserialize, Serialize};
+
+    use crate::time::DurationUs;
+
+    pub fn serialize<S>(value: &DurationUs, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        humantime_serde::Serde::from(Duration::from(*value)).serialize(serializer)
+    }
+
+    pub fn deserialize<'de, D>(deserializer: D) -> Result<DurationUs, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        let value = humantime_serde::Serde::<Duration>::deserialize(deserializer)?;
+        value.into_inner().try_into().map_err(D::Error::custom)
+    }
+}

+ 330 - 0
lazer/sdk/rust/protocol/src/time/tests.rs

@@ -0,0 +1,330 @@
+use super::*;
+
+type ChronoUtcDateTime = chrono::DateTime<chrono::Utc>;
+
+#[test]
+fn timestamp_constructors() {
+    assert!(TimestampUs::now() > TimestampUs::UNIX_EPOCH);
+    assert!(TimestampUs::now() < TimestampUs::MAX);
+
+    assert_eq!(TimestampUs::from_micros(12345).as_micros(), 12345);
+    assert_eq!(TimestampUs::from_micros(12345).as_nanos(), 12345000);
+    assert_eq!(TimestampUs::from_micros(12345).as_millis(), 12);
+    assert_eq!(TimestampUs::from_micros(12345).as_secs(), 0);
+
+    assert_eq!(TimestampUs::from_micros(123456789).as_millis(), 123456);
+    assert_eq!(TimestampUs::from_micros(123456789).as_secs(), 123);
+
+    assert_eq!(
+        TimestampUs::from_nanos(1234567890).unwrap().as_nanos(),
+        1234567000
+    );
+    assert_eq!(
+        TimestampUs::from_nanos(1234567890).unwrap().as_nanos_i128(),
+        1234567000
+    );
+
+    assert_eq!(TimestampUs::from_millis(25).unwrap().as_millis(), 25);
+    assert_eq!(TimestampUs::from_millis(25).unwrap().as_micros(), 25000);
+
+    assert_eq!(TimestampUs::from_secs(25).unwrap().as_secs(), 25);
+    assert_eq!(TimestampUs::from_secs(25).unwrap().as_millis(), 25000);
+    assert_eq!(TimestampUs::from_secs(25).unwrap().as_micros(), 25000000);
+
+    TimestampUs::from_nanos(u128::from(u64::MAX) * 1000 + 5000).unwrap_err();
+    TimestampUs::from_millis(5_000_000_000_000_000_000).unwrap_err();
+    TimestampUs::from_secs(5_000_000_000_000_000).unwrap_err();
+}
+
+#[test]
+fn duration_constructors() {
+    assert_eq!(DurationUs::from_micros(12345).as_micros(), 12345);
+    assert_eq!(DurationUs::from_micros(12345).as_nanos(), 12345000);
+    assert_eq!(DurationUs::from_micros(12345).as_millis(), 12);
+    assert_eq!(DurationUs::from_micros(12345).as_secs(), 0);
+
+    assert_eq!(DurationUs::from_micros(123456789).as_millis(), 123456);
+    assert_eq!(DurationUs::from_micros(123456789).as_secs(), 123);
+
+    assert_eq!(
+        DurationUs::from_nanos(1234567890).unwrap().as_nanos(),
+        1234567000
+    );
+    assert_eq!(
+        DurationUs::from_nanos(1234567890).unwrap().as_nanos_i128(),
+        1234567000
+    );
+
+    assert_eq!(DurationUs::from_millis(25).unwrap().as_millis(), 25);
+    assert_eq!(DurationUs::from_millis(25).unwrap().as_micros(), 25000);
+
+    assert_eq!(DurationUs::from_secs(25).unwrap().as_secs(), 25);
+    assert_eq!(DurationUs::from_secs(25).unwrap().as_millis(), 25000);
+    assert_eq!(DurationUs::from_secs(25).unwrap().as_micros(), 25000000);
+
+    DurationUs::from_nanos(u128::from(u64::MAX) * 1000 + 5000).unwrap_err();
+    DurationUs::from_millis(5_000_000_000_000_000_000).unwrap_err();
+    DurationUs::from_secs(5_000_000_000_000_000).unwrap_err();
+
+    assert_eq!(DurationUs::from_millis_u32(42).as_micros(), 42_000);
+    assert_eq!(DurationUs::from_secs_u32(42).as_micros(), 42_000_000);
+    assert_eq!(DurationUs::from_days_u16(42).as_micros(), 3_628_800_000_000);
+
+    assert_eq!(
+        DurationUs::from_millis_u32(u32::MAX).as_micros(),
+        4_294_967_295_000
+    );
+    assert_eq!(
+        DurationUs::from_secs_u32(u32::MAX).as_micros(),
+        4_294_967_295_000_000
+    );
+    assert_eq!(
+        DurationUs::from_days_u16(u16::MAX).as_micros(),
+        5_662_224_000_000_000
+    );
+}
+
+#[test]
+#[allow(clippy::bool_assert_comparison)]
+fn timestamp_ops() {
+    assert_eq!(
+        TimestampUs::from_micros(123)
+            .checked_sub(DurationUs::from_micros(23))
+            .unwrap(),
+        TimestampUs::from_micros(100)
+    );
+    TimestampUs::from_micros(123)
+        .checked_sub(DurationUs::from_micros(223))
+        .unwrap_err();
+
+    assert_eq!(
+        TimestampUs::from_micros(123)
+            .checked_add(DurationUs::from_micros(23))
+            .unwrap(),
+        TimestampUs::from_micros(146)
+    );
+    TimestampUs::from_micros(u64::MAX - 5)
+        .checked_add(DurationUs::from_micros(223))
+        .unwrap_err();
+
+    assert_eq!(
+        TimestampUs::from_micros(123)
+            .duration_since(TimestampUs::from_micros(23))
+            .unwrap(),
+        DurationUs::from_micros(100)
+    );
+    TimestampUs::from_micros(123)
+        .duration_since(TimestampUs::from_micros(223))
+        .unwrap_err();
+
+    assert_eq!(
+        TimestampUs::from_micros(123).saturating_duration_since(TimestampUs::from_micros(23)),
+        DurationUs::from_micros(100)
+    );
+    assert_eq!(
+        TimestampUs::from_micros(123).saturating_duration_since(TimestampUs::from_micros(223)),
+        DurationUs::ZERO
+    );
+
+    assert_eq!(
+        TimestampUs::from_micros(123).saturating_add(DurationUs::from_micros(100)),
+        TimestampUs::from_micros(223)
+    );
+    assert_eq!(
+        TimestampUs::from_micros(u64::MAX - 100).saturating_add(DurationUs::from_micros(200)),
+        TimestampUs::from_micros(u64::MAX)
+    );
+    assert_eq!(
+        TimestampUs::from_micros(123).saturating_sub(DurationUs::from_micros(100)),
+        TimestampUs::from_micros(23)
+    );
+    assert_eq!(
+        TimestampUs::from_micros(123).saturating_sub(DurationUs::from_micros(200)),
+        TimestampUs::from_micros(0)
+    );
+    assert_eq!(
+        TimestampUs::from_micros(123).is_multiple_of(DurationUs::from_micros(200)),
+        false
+    );
+    assert_eq!(
+        TimestampUs::from_micros(400).is_multiple_of(DurationUs::from_micros(200)),
+        true
+    );
+    assert_eq!(
+        TimestampUs::from_micros(400).is_multiple_of(DurationUs::from_micros(0)),
+        true
+    );
+    assert_eq!(
+        TimestampUs::from_micros(400)
+            .next_multiple_of(DurationUs::from_micros(200))
+            .unwrap(),
+        TimestampUs::from_micros(400)
+    );
+    assert_eq!(
+        TimestampUs::from_micros(400)
+            .previous_multiple_of(DurationUs::from_micros(200))
+            .unwrap(),
+        TimestampUs::from_micros(400)
+    );
+    assert_eq!(
+        TimestampUs::from_micros(678)
+            .next_multiple_of(DurationUs::from_micros(200))
+            .unwrap(),
+        TimestampUs::from_micros(800)
+    );
+    assert_eq!(
+        TimestampUs::from_micros(678)
+            .previous_multiple_of(DurationUs::from_micros(200))
+            .unwrap(),
+        TimestampUs::from_micros(600)
+    );
+    TimestampUs::from_micros(678)
+        .previous_multiple_of(DurationUs::from_micros(0))
+        .unwrap_err();
+    TimestampUs::from_micros(678)
+        .next_multiple_of(DurationUs::from_micros(0))
+        .unwrap_err();
+    TimestampUs::from_micros(u64::MAX - 5)
+        .next_multiple_of(DurationUs::from_micros(1000))
+        .unwrap_err();
+}
+
+#[test]
+#[allow(clippy::bool_assert_comparison)]
+fn duration_ops() {
+    assert_eq!(
+        DurationUs::from_micros(400).is_multiple_of(DurationUs::from_micros(200)),
+        true
+    );
+    assert_eq!(
+        DurationUs::from_micros(400).is_multiple_of(DurationUs::from_micros(300)),
+        false
+    );
+    assert_eq!(
+        DurationUs::from_micros(400).is_multiple_of(DurationUs::from_micros(0)),
+        true
+    );
+
+    assert_eq!(
+        DurationUs::from_micros(123)
+            .checked_add(DurationUs::from_micros(100))
+            .unwrap(),
+        DurationUs::from_micros(223)
+    );
+    DurationUs::from_micros(u64::MAX - 5)
+        .checked_add(DurationUs::from_micros(100))
+        .unwrap_err();
+
+    assert_eq!(
+        DurationUs::from_micros(123)
+            .checked_sub(DurationUs::from_micros(100))
+            .unwrap(),
+        DurationUs::from_micros(23)
+    );
+    DurationUs::from_micros(123)
+        .checked_sub(DurationUs::from_micros(200))
+        .unwrap_err();
+
+    assert_eq!(
+        DurationUs::from_micros(123).checked_mul(100).unwrap(),
+        DurationUs::from_micros(12300)
+    );
+    DurationUs::from_micros(u64::MAX - 5)
+        .checked_mul(100)
+        .unwrap_err();
+    assert_eq!(
+        DurationUs::from_micros(123).checked_div(100).unwrap(),
+        DurationUs::from_micros(1)
+    );
+    assert_eq!(
+        DurationUs::from_micros(12300).checked_div(100).unwrap(),
+        DurationUs::from_micros(123)
+    );
+    DurationUs::from_micros(123).checked_div(0).unwrap_err();
+
+    assert!(DurationUs::ZERO.is_zero());
+    assert!(!DurationUs::ZERO.is_positive());
+
+    assert!(DurationUs::from_micros(5).is_positive());
+    assert!(!DurationUs::from_micros(5).is_zero());
+}
+
+#[test]
+fn timestamp_conversions() {
+    let system_time = SystemTime::UNIX_EPOCH + Duration::from_micros(3_456_789_123_456_789);
+    let ts = TimestampUs::try_from(system_time).unwrap();
+    assert_eq!(ts, TimestampUs::from_micros(3_456_789_123_456_789));
+    assert_eq!(SystemTime::try_from(ts).unwrap(), system_time);
+
+    let proto_ts = ProtobufTimestamp::from(ts);
+    assert_eq!(proto_ts.seconds, 3_456_789_123);
+    assert_eq!(proto_ts.nanos, 456_789_000);
+    assert_eq!(TimestampUs::try_from(&proto_ts).unwrap(), ts);
+    assert_eq!(TimestampUs::try_from(proto_ts).unwrap(), ts);
+
+    let chrono_dt: ChronoUtcDateTime = "2079-07-17T03:12:03.456789Z".parse().unwrap();
+    assert_eq!(ChronoUtcDateTime::try_from(ts).unwrap(), chrono_dt);
+    assert_eq!(TimestampUs::try_from(chrono_dt).unwrap(), ts);
+}
+
+#[test]
+fn duration_conversions() {
+    let duration = DurationUs::from_micros(123_456_789);
+    let std_duration = Duration::from(duration);
+    assert_eq!(format!("{std_duration:?}"), "123.456789s");
+    assert_eq!(DurationUs::try_from(std_duration).unwrap(), duration);
+
+    let proto_duration = ProtobufDuration::from(duration);
+    assert_eq!(proto_duration.seconds, 123);
+    assert_eq!(proto_duration.nanos, 456_789_000);
+    assert_eq!(DurationUs::try_from(proto_duration).unwrap(), duration);
+}
+
+#[derive(Debug, PartialEq, Deserialize, Serialize)]
+struct Test1 {
+    t1: TimestampUs,
+    d1: DurationUs,
+    #[serde(with = "super::duration_us_serde_humantime")]
+    d2: DurationUs,
+}
+
+#[test]
+fn time_serde() {
+    let test1 = Test1 {
+        t1: TimestampUs::from_micros(123456789),
+        d1: DurationUs::from_micros(123456789),
+        d2: DurationUs::from_micros(123456789),
+    };
+
+    let json = serde_json::to_string(&test1).unwrap();
+    assert_eq!(
+        json,
+        r#"{"t1":123456789,"d1":123456789,"d2":"2m 3s 456ms 789us"}"#
+    );
+    assert_eq!(serde_json::from_str::<Test1>(&json).unwrap(), test1);
+}
+
+#[cfg(feature = "mry")]
+#[test]
+#[mry::lock(TimestampUs::now)]
+fn now_tests() {
+    use std::sync::atomic::{AtomicU64, Ordering};
+    use std::sync::Arc;
+
+    let now = Arc::new(AtomicU64::new(42));
+    let now2 = Arc::clone(&now);
+    TimestampUs::mock_now()
+        .returns_with(move || TimestampUs::from_micros(now2.load(Ordering::Relaxed)));
+
+    assert_eq!(TimestampUs::now().as_micros(), 42);
+
+    now.store(45, Ordering::Relaxed);
+    let s = TimestampUs::now();
+    now.store(95, Ordering::Relaxed);
+    assert_eq!(s.elapsed().unwrap(), DurationUs::from_micros(50));
+    assert_eq!(s.saturating_elapsed(), DurationUs::from_micros(50));
+
+    now.store(35, Ordering::Relaxed);
+    s.elapsed().unwrap_err();
+    assert_eq!(s.saturating_elapsed(), DurationUs::ZERO);
+}

+ 8 - 1
target_chains/ethereum/contracts/hardhat.config.ts

@@ -60,6 +60,13 @@ module.exports = {
       verifyURL:
         "https://api-explorer-verify.testnet.abs.xyz/contract_verification",
     },
+    abstract: {
+      url: "https://api.mainnet.abs.xyz",
+      ethNetwork: "mainnet",
+      zksync: true,
+      verifyURL:
+        "https://api-explorer-verify.mainnet.abs.xyz/contract_verification",
+    },
     mathMainnet: {
       url: "https://redacted.master.dev/",
       ethNetwork: "mainnet",
@@ -112,7 +119,7 @@ module.exports = {
     ],
   },
   solidity: {
-    version: "0.8.29",
+    version: "0.8.20",
     evmVersion: "paris",
     settings: {
       optimizer: {

+ 24 - 153
target_chains/stylus/Cargo.lock

@@ -193,7 +193,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "762414662d793d7aaa36ee3af6928b6be23227df1681ce9c039f6f11daadef64"
 dependencies = [
  "alloy-primitives 0.8.20",
- "alloy-sol-types 0.8.20",
+ "alloy-sol-types",
  "serde",
  "serde_json",
  "thiserror 2.0.12",
@@ -216,7 +216,7 @@ dependencies = [
  "alloy-rpc-types-eth",
  "alloy-serde",
  "alloy-signer",
- "alloy-sol-types 0.8.20",
+ "alloy-sol-types",
  "async-trait",
  "auto_impl",
  "futures-utils-wasm",
@@ -317,7 +317,7 @@ dependencies = [
  "alloy-primitives 0.8.20",
  "alloy-rpc-client",
  "alloy-rpc-types-eth",
- "alloy-sol-types 0.8.20",
+ "alloy-sol-types",
  "alloy-transport",
  "alloy-transport-http",
  "async-stream",
@@ -418,7 +418,7 @@ dependencies = [
  "alloy-primitives 0.8.20",
  "alloy-rlp 0.3.12",
  "alloy-serde",
- "alloy-sol-types 0.8.20",
+ "alloy-sol-types",
  "itertools 0.14.0",
  "serde",
  "serde_json",
@@ -467,59 +467,27 @@ dependencies = [
  "thiserror 2.0.12",
 ]
 
-[[package]]
-name = "alloy-sol-macro"
-version = "0.7.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b40397ddcdcc266f59f959770f601ce1280e699a91fc1862f29cef91707cd09"
-dependencies = [
- "alloy-sol-macro-expander 0.7.7",
- "alloy-sol-macro-input 0.7.7",
- "proc-macro-error",
- "proc-macro2",
- "quote",
- "syn 2.0.101",
-]
-
 [[package]]
 name = "alloy-sol-macro"
 version = "0.8.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e10ae8e9a91d328ae954c22542415303919aabe976fe7a92eb06db1b68fd59f2"
 dependencies = [
- "alloy-sol-macro-expander 0.8.25",
- "alloy-sol-macro-input 0.8.25",
+ "alloy-sol-macro-expander",
+ "alloy-sol-macro-input",
  "proc-macro-error2",
  "proc-macro2",
  "quote",
  "syn 2.0.101",
 ]
 
-[[package]]
-name = "alloy-sol-macro-expander"
-version = "0.7.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "867a5469d61480fea08c7333ffeca52d5b621f5ca2e44f271b117ec1fc9a0525"
-dependencies = [
- "alloy-sol-macro-input 0.7.7",
- "const-hex",
- "heck",
- "indexmap",
- "proc-macro-error",
- "proc-macro2",
- "quote",
- "syn 2.0.101",
- "syn-solidity 0.7.7",
- "tiny-keccak",
-]
-
 [[package]]
 name = "alloy-sol-macro-expander"
 version = "0.8.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "83ad5da86c127751bc607c174d6c9fe9b85ef0889a9ca0c641735d77d4f98f26"
 dependencies = [
- "alloy-sol-macro-input 0.8.25",
+ "alloy-sol-macro-input",
  "const-hex",
  "heck",
  "indexmap",
@@ -527,25 +495,10 @@ dependencies = [
  "proc-macro2",
  "quote",
  "syn 2.0.101",
- "syn-solidity 0.8.25",
+ "syn-solidity",
  "tiny-keccak",
 ]
 
-[[package]]
-name = "alloy-sol-macro-input"
-version = "0.7.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e482dc33a32b6fadbc0f599adea520bd3aaa585c141a80b404d0a3e3fa72528"
-dependencies = [
- "const-hex",
- "dunce",
- "heck",
- "proc-macro2",
- "quote",
- "syn 2.0.101",
- "syn-solidity 0.7.7",
-]
-
 [[package]]
 name = "alloy-sol-macro-input"
 version = "0.8.25"
@@ -559,7 +512,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "syn 2.0.101",
- "syn-solidity 0.8.25",
+ "syn-solidity",
 ]
 
 [[package]]
@@ -572,17 +525,6 @@ dependencies = [
  "winnow",
 ]
 
-[[package]]
-name = "alloy-sol-types"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a49042c6d3b66a9fe6b2b5a8bf0d39fc2ae1ee0310a2a26ffedd79fb097878dd"
-dependencies = [
- "alloy-primitives 0.7.6",
- "alloy-sol-macro 0.7.7",
- "const-hex",
-]
-
 [[package]]
 name = "alloy-sol-types"
 version = "0.8.20"
@@ -591,7 +533,7 @@ checksum = "75f306fc801b3aa2e3c4785b7b5252ec8b19f77b30e3b75babfd23849c81bd8c"
 dependencies = [
  "alloy-json-abi",
  "alloy-primitives 0.8.20",
- "alloy-sol-macro 0.8.25",
+ "alloy-sol-macro",
  "const-hex",
  "serde",
 ]
@@ -3292,19 +3234,6 @@ version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dce6dd36094cac388f119d2e9dc82dc730ef91c32a6222170d630e5414b956e6"
 
-[[package]]
-name = "motsu"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "744174e5011fed86212d90c1120037da87e0c45fee7a5861c2b3105e41283810"
-dependencies = [
- "const-hex",
- "motsu-proc 0.1.1",
- "once_cell",
- "stylus-sdk 0.6.0",
- "tiny-keccak",
-]
-
 [[package]]
 name = "motsu"
 version = "0.9.1"
@@ -3313,28 +3242,17 @@ checksum = "31eab998c3d9d9e2627e291cc44dc98a8f7c30f63bd21181f6468e58ceb70185"
 dependencies = [
  "alloy-primitives 0.8.20",
  "alloy-signer-local",
- "alloy-sol-types 0.8.20",
+ "alloy-sol-types",
  "const-hex",
  "dashmap",
  "k256",
- "motsu-proc 0.9.0",
+ "motsu-proc",
  "once_cell",
  "revm-precompile",
- "stylus-sdk 0.9.0",
+ "stylus-sdk",
  "tiny-keccak",
 ]
 
-[[package]]
-name = "motsu-proc"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00b990e6013802b44b3d0c0806c80b40bb36a2ecd70ca380a5df09fd7a201576"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.101",
-]
-
 [[package]]
 name = "motsu-proc"
 version = "0.9.0"
@@ -3982,17 +3900,17 @@ name = "pyth-receiver-stylus"
 version = "0.1.11"
 dependencies = [
  "alloy-primitives 0.8.20",
- "alloy-sol-types 0.8.20",
+ "alloy-sol-types",
  "byteorder",
  "dotenv",
  "ethers",
  "eyre",
  "hex",
  "mock_instant",
- "motsu 0.9.1",
+ "motsu",
  "pythnet-sdk",
  "serde",
- "stylus-sdk 0.9.0",
+ "stylus-sdk",
  "tokio",
  "wormhole-contract",
  "wormhole-vaas",
@@ -5079,30 +4997,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b96d0a4daa641fb5904cc3302c90b2d160f59c62aae7a19c2c6b94e28e3ab93e"
 dependencies = [
  "alloy-primitives 0.8.20",
- "alloy-sol-types 0.8.20",
+ "alloy-sol-types",
  "cfg-if 1.0.0",
  "dyn-clone",
 ]
 
-[[package]]
-name = "stylus-proc"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce199dd40c83734b835a6c952e29edd9a7e9370b438829c6b2cd908fd3acfb1e"
-dependencies = [
- "alloy-primitives 0.7.6",
- "alloy-sol-types 0.7.6",
- "cfg-if 1.0.0",
- "convert_case 0.6.0",
- "lazy_static",
- "proc-macro2",
- "quote",
- "regex",
- "sha3",
- "syn 1.0.109",
- "syn-solidity 0.7.7",
-]
-
 [[package]]
 name = "stylus-proc"
 version = "0.9.0"
@@ -5110,7 +5009,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c87cad1205aac3d46a3bb303ebe24976de3e88652d001a2e0e489b1e2bd6d0b9"
 dependencies = [
  "alloy-primitives 0.8.20",
- "alloy-sol-types 0.8.20",
+ "alloy-sol-types",
  "cfg-if 1.0.0",
  "convert_case 0.6.0",
  "lazy_static",
@@ -5120,26 +5019,10 @@ dependencies = [
  "regex",
  "sha3",
  "syn 2.0.101",
- "syn-solidity 0.8.25",
+ "syn-solidity",
  "trybuild",
 ]
 
-[[package]]
-name = "stylus-sdk"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26042693706e29fb7e3cf3d71c99534ac97fca98b6f81ba77ab658022ab2e210"
-dependencies = [
- "alloy-primitives 0.7.6",
- "alloy-sol-types 0.7.6",
- "cfg-if 1.0.0",
- "derivative",
- "hex",
- "keccak-const",
- "lazy_static",
- "stylus-proc 0.6.1",
-]
-
 [[package]]
 name = "stylus-sdk"
 version = "0.9.0"
@@ -5147,7 +5030,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "38519923dae967837b90f00cd669b9d6df778ec1f0df849089f4dd456be96bf8"
 dependencies = [
  "alloy-primitives 0.8.20",
- "alloy-sol-types 0.8.20",
+ "alloy-sol-types",
  "cfg-if 1.0.0",
  "clap",
  "derivative",
@@ -5158,7 +5041,7 @@ dependencies = [
  "rclite",
  "regex",
  "stylus-core",
- "stylus-proc 0.9.0",
+ "stylus-proc",
  "stylus-test",
 ]
 
@@ -5170,7 +5053,7 @@ checksum = "abef8a75532dab6480d83577e47f4db429e7f0330825b3e167a3c9ddb363fd1f"
 dependencies = [
  "alloy-primitives 0.8.20",
  "alloy-provider",
- "alloy-sol-types 0.8.20",
+ "alloy-sol-types",
  "stylus-core",
  "tokio",
  "url",
@@ -5237,18 +5120,6 @@ dependencies = [
  "unicode-ident",
 ]
 
-[[package]]
-name = "syn-solidity"
-version = "0.7.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c837dc8852cb7074e46b444afb81783140dab12c58867b49fb3898fbafedf7ea"
-dependencies = [
- "paste",
- "proc-macro2",
- "quote",
- "syn 2.0.101",
-]
-
 [[package]]
 name = "syn-solidity"
 version = "0.8.25"
@@ -6221,8 +6092,8 @@ dependencies = [
  "base64 0.21.7",
  "k256",
  "mini-alloc 0.4.2",
- "motsu 0.1.0",
- "stylus-sdk 0.9.0",
+ "motsu",
+ "stylus-sdk",
 ]
 
 [[package]]

+ 1 - 1
target_chains/stylus/Cargo.toml

@@ -14,7 +14,7 @@ version = "0.1.0"
 stylus-sdk = { version = "0.9.0", default-features = false }
 alloy-primitives = { version = "0.7.6", default-features = false }
 mini-alloc = { version = "0.4.2", default-features = false }
-motsu = "0.1.0"
+motsu = "0.9.0"
 
 k256 = { version = "0.13.3", default-features = false, features = ["ecdsa"] }
 

+ 145 - 257
target_chains/stylus/contracts/wormhole/src/lib.rs

@@ -543,6 +543,8 @@ impl IWormhole for WormholeContract {
 mod tests {
     use super::*;
     use alloc::vec;
+    use motsu::prelude::*;
+    use motsu::prelude::Contract;
     use core::str::FromStr;
     use k256::ecdsa::SigningKey;
     use motsu::prelude::DefaultStorage;
@@ -663,83 +665,40 @@ mod tests {
     }
 
     #[cfg(test)]
-    fn deploy_with_test_guardian() -> WormholeContract {
-        let mut contract = WormholeContract::default();
+    fn deploy_with_test_guardian(wormhole_contract: &Contract<WormholeContract>, alice: &Address) {
         let guardians = vec![test_guardian_address1()];
-        let governance_contract =
-            Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
-        match contract.store_gs(0, guardians.clone(), 0) {
+        let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
+
+        match wormhole_contract.sender(*alice).store_gs(0, guardians.clone(), 0) {
             Ok(_) => {}
             Err(_) => unreachable!(),
         }
-        contract
-            .initialize(
-                guardians,
-                1,
-                CHAIN_ID,
-                GOVERNANCE_CHAIN_ID,
-                governance_contract,
-            )
-            .unwrap();
-        contract
+        wormhole_contract.sender(*alice).initialize(guardians, 1, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract).unwrap();
     }
 
     #[cfg(test)]
-    fn deploy_with_current_mainnet_guardians() -> WormholeContract {
-        let mut contract = WormholeContract::default();
+    fn deploy_with_current_mainnet_guardians(wormhole_contract: &Contract<WormholeContract>, alice: &Address) {
         let guardians = current_guardians();
-        let governance_contract =
-            Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
-        contract
-            .initialize(
-                guardians,
-                0,
-                CHAIN_ID,
-                GOVERNANCE_CHAIN_ID,
-                governance_contract,
-            )
-            .unwrap();
-        let result = contract.store_gs(4, current_guardians(), 0);
+        let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
+        wormhole_contract.sender(*alice).initialize(guardians, 0, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract).unwrap();
+        let result = wormhole_contract.sender(*alice).store_gs(4, current_guardians(), 0);
         if let Err(_) = result {
             panic!("Error deploying mainnet guardians");
         }
-        contract
     }
 
     #[cfg(test)]
-    fn deploy_with_mainnet_guardian_set0() -> WormholeContract {
-        let mut contract = WormholeContract::default();
+    fn deploy_with_mainnet_guardian_set0(wormhole_contract: &Contract<WormholeContract>, alice: &Address) {
         let guardians = guardian_set0();
-        let governance_contract =
-            Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
-        contract
-            .initialize(
-                guardians,
-                0,
-                CHAIN_ID,
-                GOVERNANCE_CHAIN_ID,
-                governance_contract,
-            )
-            .unwrap();
-        contract
+        let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
+        wormhole_contract.sender(*alice).initialize(guardians, 0, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract).unwrap();
     }
 
     #[cfg(test)]
-    fn deploy_with_mainnet_guardians() -> WormholeContract {
-        let mut contract = WormholeContract::default();
+    fn deploy_with_mainnet_guardians(wormhole_contract: &Contract<WormholeContract>, alice: &Address) {
         let guardians = guardian_set4();
-        let governance_contract =
-            Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
-        contract
-            .initialize(
-                guardians,
-                0,
-                CHAIN_ID,
-                GOVERNANCE_CHAIN_ID,
-                governance_contract,
-            )
-            .unwrap();
-        contract
+        let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
+        wormhole_contract.sender(*alice).initialize(guardians, 0, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract).unwrap();
     }
 
     #[cfg(test)]
@@ -874,12 +833,10 @@ mod tests {
     }
 
     #[motsu::test]
-    fn test_vaa_invalid_guardian_set_idx() {
-        let contract = deploy_with_current_mainnet_guardians();
-        let test_vaa = create_vaa_bytes(
-            "AQAHHHQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==",
-        );
-        let result = contract.parse_and_verify_vm(test_vaa);
+    fn test_vaa_invalid_guardian_set_idx(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+        deploy_with_current_mainnet_guardians(&wormhole_contract, &alice);
+        let test_vaa = create_vaa_bytes("AQAHHHQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==");
+        let result = wormhole_contract.sender(alice).parse_and_verify_vm(test_vaa);
         assert!(matches!(result, Err(ref err) if err == &vec![1]));
     }
 
@@ -894,55 +851,49 @@ mod tests {
     }
 
     #[motsu::test]
-    fn test_verification_multiple_guardian_sets() {
-        let mut contract = deploy_with_current_mainnet_guardians();
+    fn test_verification_multiple_guardian_sets(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+        deploy_with_current_mainnet_guardians(&wormhole_contract, &alice);
 
-        let store_result = contract.store_gs(4, current_guardians(), 0);
+        let store_result = wormhole_contract.sender(alice).store_gs(4, current_guardians(), 0);
         if let Err(_) = store_result {
             panic!("Error deploying multiple guardian sets");
         }
 
-        let test_vaa = create_vaa_bytes(
-            "AQAAAAQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==",
-        );
-        let result = contract.parse_and_verify_vm(test_vaa);
+        let test_vaa = create_vaa_bytes("AQAAAAQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==");
+        let result = wormhole_contract.sender(alice).parse_and_verify_vm(test_vaa);
         assert!(result.is_ok());
     }
 
     #[motsu::test]
-    fn test_verification_incorrect_guardian_set() {
-        let mut contract = deploy_with_current_mainnet_guardians();
+    fn test_verification_incorrect_guardian_set(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+        deploy_with_current_mainnet_guardians(&wormhole_contract, &alice);
 
-        let store_result = contract.store_gs(4, mock_guardian_set13(), 0);
+        let store_result = wormhole_contract.sender(alice).store_gs(4, mock_guardian_set13(), 0);
         if let Err(_) = store_result {
             panic!("Error deploying guardian set");
         }
 
-        let test_vaa = create_vaa_bytes(
-            "AQAAAAQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==",
-        );
-        let result = contract.parse_and_verify_vm(test_vaa);
+        let test_vaa = create_vaa_bytes("AQAAAAQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==");
+        let result = wormhole_contract.sender(alice).parse_and_verify_vm(test_vaa);
         assert!(result.is_err());
     }
 
     #[motsu::test]
-    fn test_wormhole_guardian_set_vaa_verification() {
-        let contract = deploy_with_current_mainnet_guardians();
-        let test_vaa = create_vaa_bytes(
-            "AQAAAAQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==",
-        );
-        let result = contract.parse_and_verify_vm(test_vaa);
+    fn test_wormhole_guardian_set_vaa_verification(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+        deploy_with_current_mainnet_guardians(&wormhole_contract, &alice);
+        let test_vaa = create_vaa_bytes("AQAAAAQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==");
+        let result = wormhole_contract.sender(alice).parse_and_verify_vm(test_vaa);
         assert!(result.is_ok());
     }
 
     #[motsu::test]
-    fn test_get_guardian_set_works() {
-        let contract = deploy_with_mainnet_guardian_set0();
+    fn test_get_guardian_set_works(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+        deploy_with_mainnet_guardian_set0(&wormhole_contract, &alice);
 
-        let set0 = contract.get_gs_internal(0).unwrap();
+        let set0 = wormhole_contract.sender(alice).get_gs_internal(0).unwrap();
         assert_eq!(set0.keys, guardian_set0());
         assert_eq!(set0.expiration_time, 0);
-        assert_eq!(contract.get_current_guardian_set_index(), 0);
+        assert_eq!(wormhole_contract.sender(alice).get_current_guardian_set_index(), 0);
     }
 
     #[motsu::test]
@@ -960,36 +911,32 @@ mod tests {
     }
 
     #[motsu::test]
-    fn test_verify_vm_invalid_guardian_set() {
-        let contract = deploy_with_test_guardian();
+    fn test_verify_vm_invalid_guardian_set(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+        deploy_with_test_guardian(&wormhole_contract, &alice);
         let vaa = create_test_vaa(999, vec![]);
 
-        let result = contract.verify_vm(&vaa);
-        assert!(matches!(
-            result,
-            Err(WormholeError::InvalidGuardianSetIndex)
-        ));
+        let result = wormhole_contract.sender(alice).verify_vm(&vaa);
+        assert!(matches!(result, Err(WormholeError::InvalidGuardianSetIndex)));
     }
 
     #[motsu::test]
-    fn test_verify_vm_insufficient_signatures() {
-        let contract = deploy_with_test_guardian();
+    fn test_verify_vm_insufficient_signatures(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+        deploy_with_test_guardian(&wormhole_contract, &alice);
         let vaa = create_test_vaa(0, vec![]);
 
-        let result = contract.verify_vm(&vaa);
+        let result = wormhole_contract.sender(*alice).verify_vm(&vaa);
         assert!(matches!(result, Err(WormholeError::InsufficientSignatures)));
     }
 
     #[motsu::test]
-    fn test_verify_vm_invalid_signature_order() {
-        let mut contract = WormholeContract::default();
+    fn test_verify_vm_invalid_signature_order(wormhole_contract: Contract<WormholeContract>, alice: Address) {
         let guardians = vec![
             Address::from([0x12u8; 20]),
             Address::from([0x23u8; 20]),
             Address::from([0x34u8; 20]),
         ];
-        match contract.store_gs(0, guardians.clone(), 0) {
-            Ok(_) => {}
+        match wormhole_contract.sender(alice).store_gs(0, guardians.clone(), 0) {
+            Ok(_) => {},
             Err(_) => unreachable!(),
         }
 
@@ -1000,106 +947,96 @@ mod tests {
         ];
         let vaa = create_test_vaa(0, signatures); // Use guardian set 0
 
-        let result = contract.verify_vm(&vaa);
+        let result = wormhole_contract.sender(alice).verify_vm(&vaa);
         assert!(matches!(result, Err(WormholeError::InvalidSignature)));
     }
 
     #[motsu::test]
-    fn test_verify_vm_invalid_guardian_index() {
-        let contract = deploy_with_test_guardian();
-        let signatures = vec![create_guardian_signature(5)];
+    fn test_verify_vm_invalid_guardian_index(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+        deploy_with_test_guardian(&wormhole_contract, &alice);
+        let signatures = vec![
+            create_guardian_signature(5),
+        ];
         let vaa = create_test_vaa(0, signatures);
 
-        let result = contract.verify_vm(&vaa);
+        let result = wormhole_contract.sender(*alice).verify_vm(&vaa);
         assert!(matches!(result, Err(WormholeError::InvalidGuardianIndex)));
     }
 
     #[motsu::test]
-    fn test_signature_verification_invalid_recovery_id() {
-        let contract = WormholeContract::default();
+    fn test_signature_verification_invalid_recovery_id(wormhole_contract: Contract<WormholeContract>, alice: Address) {
         let hash = FixedBytes::default();
         let guardian_address = Address::default();
 
         let mut invalid_sig = [0u8; 65];
         invalid_sig[64] = 26;
-        let result = contract.verify_signature(
-            &hash,
-            &FixedBytes::<65>::from(invalid_sig),
-            guardian_address,
-        );
+        let result = wormhole_contract.sender(alice).verify_signature(&hash, &FixedBytes::<65>::from(invalid_sig), guardian_address);
         assert!(matches!(result, Err(WormholeError::InvalidSignature)));
     }
 
     #[motsu::test]
-    fn test_signature_verification_all_zeros() {
-        let contract = WormholeContract::default();
+    fn test_signature_verification_all_zeros(wormhole_contract: Contract<WormholeContract>, alice: Address) {
         let hash = FixedBytes::default();
         let invalid_signature = FixedBytes::<65>::default();
         let guardian_address = Address::default();
 
-        let result = contract.verify_signature(&hash, &invalid_signature, guardian_address);
+        let result = wormhole_contract.sender(alice).verify_signature(&hash, &invalid_signature, guardian_address);
         assert!(matches!(result, Err(WormholeError::InvalidSignature)));
     }
 
     #[motsu::test]
-    fn test_rejects_empty_guardian_set() {
-        let mut contract = WormholeContract::default();
+    fn test_rejects_empty_guardian_set(wormhole_contract: Contract<WormholeContract>, alice: Address) {
         let empty_guardians: Vec<Address> = vec![];
 
-        let result = contract.store_gs(0, empty_guardians, 0);
+        let result = wormhole_contract.sender(alice).store_gs(0, empty_guardians, 0);
         assert!(result.is_err());
     }
 
     #[motsu::test]
-    fn test_rejects_invalid_guardian_set_index() {
-        let contract = deploy_with_test_guardian();
+    fn test_rejects_invalid_guardian_set_index(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+        deploy_with_test_guardian(&wormhole_contract, &alice);
 
-        let result = contract.get_gs_internal(999);
+        let result = wormhole_contract.sender(*alice).get_gs_internal(999);
         assert!(result.is_err());
     }
 
     #[motsu::test]
-    fn test_submit_guardian_set_rejects_invalid_emitter() {
-        let contract = deploy_with_test_guardian();
+    fn test_submit_guardian_set_rejects_invalid_emitter(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+        deploy_with_test_guardian(&wormhole_contract, &alice);
 
         let vaa = create_test_vaa_with_emitter(0, vec![], Address::from([0x99u8; 20]));
-        let result = contract.verify_vm(&vaa);
+        let result = wormhole_contract.sender(*alice).verify_vm(&vaa);
         assert!(result.is_err());
     }
 
     #[motsu::test]
-    fn test_submit_guardian_set_rejects_wrong_index() {
-        let contract = deploy_with_mainnet_guardian_set0();
+    fn test_submit_guardian_set_rejects_wrong_index(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+        deploy_with_mainnet_guardian_set0(&wormhole_contract, &alice);
 
         let vaa = create_test_vaa(2, vec![]); // Skip index 1
-        let result = contract.verify_vm(&vaa);
-        assert!(matches!(
-            result,
-            Err(WormholeError::InvalidGuardianSetIndex)
-        ));
+        let result = wormhole_contract.sender(alice).verify_vm(&vaa);
+        assert!(matches!(result, Err(WormholeError::InvalidGuardianSetIndex)));
     }
 
     #[motsu::test]
-    fn test_deploy_rejects_empty_guardian_set() {
-        let mut contract = WormholeContract::default();
+    fn test_deploy_rejects_empty_guardian_set(wormhole_contract: Contract<WormholeContract>, alice: Address) {
         let empty_guardians: Vec<Address> = vec![];
 
-        let result = contract.initialize(empty_guardians, 0, 1, 1, Address::default());
+        let result = wormhole_contract.sender(alice).initialize(empty_guardians, 0, 1, 1, Address::default());
         assert!(result.is_err());
     }
 
     #[motsu::test]
-    fn test_submit_guardian_set_rejects_empty() {
-        let mut contract = WormholeContract::default();
+    fn test_submit_guardian_set_rejects_empty(wormhole_contract: Contract<WormholeContract>, alice: Address) {
         let empty_guardians: Vec<Address> = vec![];
 
-        let result = contract.store_gs(0, empty_guardians, 0);
+        let result = wormhole_contract.sender(alice).store_gs(0, empty_guardians, 0);
         assert!(result.is_err());
     }
 
     #[motsu::test]
-    fn test_rejects_corrupted_vaa_data() {
-        let _contract = deploy_with_mainnet_guardians();
+    fn test_rejects_corrupted_vaa_data(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+        deploy_with_mainnet_guardians(&wormhole_contract, &alice);
 
         for i in 0..10 {
             let i_u8: u8 = match i.try_into() {
@@ -1115,8 +1052,8 @@ mod tests {
     }
 
     #[motsu::test]
-    fn test_parse_and_verify_vm_rejects_corrupted_vaa() {
-        let _contract = deploy_with_mainnet_guardians();
+    fn test_parse_and_verify_vm_rejects_corrupted_vaa(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+        deploy_with_mainnet_guardians(&wormhole_contract, &alice);
 
         for i in 0..5 {
             let i_u8: u8 = match i.try_into() {
@@ -1133,32 +1070,33 @@ mod tests {
     }
 
     #[motsu::test]
-    fn test_submit_guardian_set_rejects_non_governance() {
-        let contract = deploy_with_mainnet_guardian_set0();
+    fn test_submit_guardian_set_rejects_non_governance(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+        deploy_with_mainnet_guardian_set0(&wormhole_contract, &alice);
 
         let mut vaa = create_test_vaa(0, vec![]);
         vaa.emitter_chain_id = 999; // Wrong chain
 
-        let result = contract.verify_vm(&vaa);
+        let result = wormhole_contract.sender(alice).verify_vm(&vaa);
         assert!(result.is_err());
     }
 
     #[motsu::test]
-    fn test_guardian_set_storage_and_retrieval() -> Result<(), WormholeError> {
-        let mut contract = WormholeContract::default();
-        let guardians = vec![test_guardian_address1(), test_guardian_address2()];
+    fn test_guardian_set_storage_and_retrieval(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+        let guardians = vec![
+            test_guardian_address1(),
+            test_guardian_address2(),
+        ];
 
-        let _ = contract.store_gs(0, guardians.clone(), 0);
-        let retrieved_set = contract.get_gs_internal(0)?;
+        wormhole_contract.sender(alice).store_gs(0, guardians.clone(), 0).unwrap();
+        let retrieved_set = wormhole_contract.sender(alice).get_gs_internal(0).unwrap();
 
         assert_eq!(retrieved_set.keys, guardians);
         assert_eq!(retrieved_set.expiration_time, 0);
-
-        Ok(())
     }
 
     #[motsu::test]
-    fn test_guardian_key_computation() {
+    fn test_guardian_key_computation(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+
         let set_index = 0u32;
         let guardian_index = 1u8;
         let key_data = [&set_index.to_be_bytes()[..], &[guardian_index]].concat();
@@ -1171,24 +1109,31 @@ mod tests {
     }
 
     #[motsu::test]
-    fn test_multiple_guardian_sets() {
-        let mut contract = WormholeContract::default();
+    fn test_multiple_guardian_sets(wormhole_contract: Contract<WormholeContract>, alice: Address) {
 
-        contract.store_gs(0, guardian_set0(), 0).unwrap();
-        contract.store_gs(4, guardian_set4(), 0).unwrap();
+        wormhole_contract.sender(alice)
+            .store_gs(0, guardian_set0(), 0)
+            .unwrap();
+        wormhole_contract.sender(alice)
+            .store_gs(4, guardian_set4(), 0)
+            .unwrap();
 
-        let set0 = contract.get_gs_internal(0).unwrap();
-        let set4 = contract.get_gs_internal(4).unwrap();
+        let set0 = wormhole_contract.sender(alice).get_gs_internal(0)
+            .unwrap();
+        let set4 = wormhole_contract.sender(alice).get_gs_internal(4)
+            .unwrap();
 
         assert_eq!(set0.keys, guardian_set0());
         assert_eq!(set4.keys, guardian_set4());
     }
 
     #[motsu::test]
-    fn test_verify_vm_with_valid_signatures() {
-        let mut contract = WormholeContract::default();
-        let guardians = vec![test_guardian_address1(), test_guardian_address2()];
-        match contract.store_gs(0, guardians.clone(), 0) {
+    fn test_verify_vm_with_valid_signatures(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+        let guardians = vec![
+            test_guardian_address1(),
+            test_guardian_address2(),
+        ];
+        match wormhole_contract.sender(alice).store_gs(0, guardians.clone(), 0) {
             Ok(()) => (),
             Err(_) => unreachable!(),
         }
@@ -1211,49 +1156,42 @@ mod tests {
             hash,
         };
 
-        let _result = contract.verify_vm(&vaa);
+        let _result = wormhole_contract.sender(alice).verify_vm(&vaa);
     }
 
     #[motsu::test]
-    fn test_chain_id_governance_values() {
-        let contract = deploy_with_mainnet_guardians();
+    fn test_chain_id_governance_values(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+        deploy_with_mainnet_guardians(&wormhole_contract, &alice);
 
-        assert_eq!(contract.chain_id(), CHAIN_ID);
+        assert_eq!(wormhole_contract.sender(alice).chain_id(), CHAIN_ID);
 
-        assert_eq!(contract.governance_chain_id(), GOVERNANCE_CHAIN_ID);
+        assert_eq!(wormhole_contract.sender(alice).governance_chain_id(), GOVERNANCE_CHAIN_ID);
 
-        let gov_contract = contract.governance_contract();
+        let gov_contract = wormhole_contract.sender(alice).governance_contract();
         let expected = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
         assert_eq!(gov_contract, expected);
     }
 
     #[motsu::test]
-    fn test_governance_action_consumed() {
-        let contract = deploy_with_mainnet_guardians();
+    fn test_governance_action_consumed(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+        deploy_with_mainnet_guardians(&wormhole_contract, &alice);
 
         let test_hash = vec![0u8; 32];
-        assert_eq!(contract.governance_action_is_consumed(test_hash), false);
+        assert_eq!(wormhole_contract.sender(alice).governance_action_is_consumed(test_hash), false);
     }
 
     #[motsu::test]
-    fn test_initialize_contract_like_shell_script() {
-        let mut contract = WormholeContract::default();
+    fn test_initialize_contract_like_shell_script(wormhole_contract: Contract<WormholeContract>, alice: Address) {
         let guardians = current_guardians();
         let governance_contract =
             Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
 
-        let result = contract.initialize(
-            guardians.clone(),
-            4,
-            CHAIN_ID,
-            GOVERNANCE_CHAIN_ID,
-            governance_contract,
-        );
+        let result = wormhole_contract.sender(alice).initialize(guardians.clone(), 4, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract);
         assert!(result.is_ok(), "Contract initialization should succeed");
     }
 
     #[motsu::test]
-    fn test_quorum_calculation_integration_test() {
+    fn test_quorum_calculation_integration_test(wormhole_contract: Contract<WormholeContract>, alice: Address) {
         let quorum_result = WormholeContract::quorum(3);
         assert_eq!(
             quorum_result, 3,
@@ -1262,25 +1200,15 @@ mod tests {
     }
 
     #[motsu::test]
-    fn test_guardian_set_retrieval_current_guardians() {
-        let mut contract = WormholeContract::default();
+    fn test_guardian_set_retrieval_current_guardians(wormhole_contract: Contract<WormholeContract>, alice: Address) {
         let guardians = current_guardians();
         let governance_contract =
             Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
 
-        let _ = contract.initialize(
-            guardians.clone(),
-            4,
-            CHAIN_ID,
-            GOVERNANCE_CHAIN_ID,
-            governance_contract,
-        );
+        let _ = wormhole_contract.sender(alice).initialize(guardians.clone(), 4, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract);
 
-        let guardian_set_result = contract.get_guardian_set(4);
-        assert!(
-            guardian_set_result.is_ok(),
-            "Guardian set retrieval should work - contract is initialized"
-        );
+        let guardian_set_result = wormhole_contract.sender(alice).get_guardian_set(4);
+        assert!(guardian_set_result.is_ok(), "Guardian set retrieval should work - contract is initialized");
 
         let guardian_set_bytes = guardian_set_result.unwrap();
         assert_eq!(
@@ -1289,93 +1217,53 @@ mod tests {
             "Should have 19 guardian addresses (20 bytes each)"
         );
 
-        assert_eq!(
-            contract.chain_id(),
-            CHAIN_ID,
-            "Chain ID should match shell script value"
-        );
+        assert_eq!(wormhole_contract.sender(alice).chain_id(), CHAIN_ID, "Chain ID should match shell script value");
 
-        assert_eq!(
-            contract.governance_chain_id(),
-            GOVERNANCE_CHAIN_ID,
-            "Governance chain ID should match shell script value"
-        );
+        assert_eq!(wormhole_contract.sender(alice).governance_chain_id(), GOVERNANCE_CHAIN_ID, "Governance chain ID should match shell script value");
 
-        assert_eq!(
-            contract.governance_contract(),
-            governance_contract,
-            "Governance contract should match shell script value"
-        );
+        assert_eq!(wormhole_contract.sender(alice).governance_contract(), governance_contract, "Governance contract should match shell script value");
 
-        assert_eq!(
-            contract.get_current_guardian_set_index(),
-            4,
-            "Current guardian set index should be 4"
-        );
+        assert_eq!(wormhole_contract.sender(alice).get_current_guardian_set_index(), 4, "Current guardian set index should be 4");
     }
 
     #[motsu::test]
-    fn test_duplicate_verification() {
-        let mut contract = WormholeContract::default();
+    fn test_duplicate_verification(wormhole_contract: Contract<WormholeContract>, alice: Address) {
         let guardians = current_guardians_duplicate();
         let governance_contract =
             Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
 
-        let _ = contract.initialize(
-            guardians.clone(),
-            4,
-            CHAIN_ID,
-            GOVERNANCE_CHAIN_ID,
-            governance_contract,
-        );
+        let _ = wormhole_contract.sender(alice).initialize(guardians.clone(), 4, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract);
 
-        let guardian_set_result = contract.get_guardian_set(4);
-        assert!(
-            guardian_set_result.is_ok(),
-            "Guardian set retrieval should work - contract is initialized"
-        );
+        let guardian_set_result = wormhole_contract.sender(alice).get_guardian_set(4);
+        assert!(guardian_set_result.is_ok(), "Guardian set retrieval should work - contract is initialized");
 
-        let test_vaa = create_vaa_bytes(
-            "AQAAAAQNAInUwKI1ItLfYeLaAibn9oXaouTs9BL3Aa9DKCFWrLu0KDaQQMQJlih0Qh7l7yH2o6kD/g9RCmRwZJ6q0OZE0t4AArCSH1wpX04N1U59tQmss2xXZilimAMKlogp7ErAhAo0LFkDogqB74+2By9rm3P5OUWlbC0lrFNut5CQQV38DGsAAxO+1nUTUc842P2afDSjdWcmjvJl2s8secQzuiW8zrdgPpbzhzWsiYXizLQBRKigDS8pWGD4vRk0fuR8H/ZkO/0BBOmDobl1BLNJx7+Pt+NWfuOUBipVFIXGxI9b3vxxH0BIec8hhxDN4m2Pd2I0klGEXKhv9plcR7VlzAsaC7ZE7QIABh4ff66tP7EHdVfZR4mTzv5B97agMcSB1eDeijpyl9JuBhbMupw7nExZNnZag/x2k6AUEWnQnfp8AoaCK7Av+icAB2Ouk9mPd1ybyju39Q8m7GMevt2f1nHVyWVsPRzdEcCuAbzjh5137DCLzVWuFUujTQJ7IJiznQb6cm2Ljk3WOXUACMa/JwRdpVKZf6eTD6O6tivqhdhMtbijlPBZX/kgVKk5Xuyv3h1SRTrNCwkMg5XOWegnCbXqjbUlo+F3qTjCalQBCxfp1itJskZmv+SXA47QivURKWzGa3mntNh0vcAXYi8FeChvoUYmfYpejmBlOkD1I73pmUsyrbYbetHa7qFu3eoBDZScdyrWp2dS5Y9L4b0who/PncVp5oFs/4J8ThHNQoXWXvys+nUc2aM+E+Fwazo2ODdI8XZz9YOGf/ZfE6iXFBYBDgckow8Nb2QD//C6MfP2Bz8zftqvt+D6Dko7v/Inb2OtCj342yjrxcvAMlCQ6lYoTIAMNemzNoqlfNyDMdB9yKoAEKebRtCm8QZSjLQ5uPk8aoQpmNwCpLhiHuzh2fqH55fcQrE6/KFttfw7VzeGUE7k3PF6xIMq0BPr3vkG2MedIh8BEQvpmYK4fChLY5JG26Kk6KuZ1eCkJAOQgdSjWasAvNgsSIlsb5mFjIkGwK9j20svLSl+OJ7I0olefXcZ2JywjgYAEu1jITMLHCMR1blXENulhApdhMfTef1aQ/USMqRVWNigausEzq49Hi2GtcQzHmZuhgnhBZEnjq9K8jsZwJk59iwBaFxZegAAAAAAATTNxrJiPzbWCugg6Vtg92ToHsLNO1e3fj+OJd3UOsNzAAAAAAATpFIAAVE6cNLnZT2Noq5nJ4VNRSf2KrRBNrlimFaXauHv3efDAAFm5RiKEwih25C20x8/vcqMPfJnjIES3909GSxaPMRXqAAAAAAAAAAAAAAAAFxIFHGlrpnuxd5M5WePQalLpUyHAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALFcwAAAAAAAAAAAAAAAAaFxdzQAAAAAAAAAAAAAAAK3MabLDE8LWvGN6+AdUvFHJdm5RAAMAAAAAAAAAAAAAAADf0SJhChSsEtk0iYwC2+wfcnCBFg==",
-        );
-        let result = contract.parse_and_verify_vm(test_vaa);
+        let test_vaa = create_vaa_bytes("AQAAAAQNAInUwKI1ItLfYeLaAibn9oXaouTs9BL3Aa9DKCFWrLu0KDaQQMQJlih0Qh7l7yH2o6kD/g9RCmRwZJ6q0OZE0t4AArCSH1wpX04N1U59tQmss2xXZilimAMKlogp7ErAhAo0LFkDogqB74+2By9rm3P5OUWlbC0lrFNut5CQQV38DGsAAxO+1nUTUc842P2afDSjdWcmjvJl2s8secQzuiW8zrdgPpbzhzWsiYXizLQBRKigDS8pWGD4vRk0fuR8H/ZkO/0BBOmDobl1BLNJx7+Pt+NWfuOUBipVFIXGxI9b3vxxH0BIec8hhxDN4m2Pd2I0klGEXKhv9plcR7VlzAsaC7ZE7QIABh4ff66tP7EHdVfZR4mTzv5B97agMcSB1eDeijpyl9JuBhbMupw7nExZNnZag/x2k6AUEWnQnfp8AoaCK7Av+icAB2Ouk9mPd1ybyju39Q8m7GMevt2f1nHVyWVsPRzdEcCuAbzjh5137DCLzVWuFUujTQJ7IJiznQb6cm2Ljk3WOXUACMa/JwRdpVKZf6eTD6O6tivqhdhMtbijlPBZX/kgVKk5Xuyv3h1SRTrNCwkMg5XOWegnCbXqjbUlo+F3qTjCalQBCxfp1itJskZmv+SXA47QivURKWzGa3mntNh0vcAXYi8FeChvoUYmfYpejmBlOkD1I73pmUsyrbYbetHa7qFu3eoBDZScdyrWp2dS5Y9L4b0who/PncVp5oFs/4J8ThHNQoXWXvys+nUc2aM+E+Fwazo2ODdI8XZz9YOGf/ZfE6iXFBYBDgckow8Nb2QD//C6MfP2Bz8zftqvt+D6Dko7v/Inb2OtCj342yjrxcvAMlCQ6lYoTIAMNemzNoqlfNyDMdB9yKoAEKebRtCm8QZSjLQ5uPk8aoQpmNwCpLhiHuzh2fqH55fcQrE6/KFttfw7VzeGUE7k3PF6xIMq0BPr3vkG2MedIh8BEQvpmYK4fChLY5JG26Kk6KuZ1eCkJAOQgdSjWasAvNgsSIlsb5mFjIkGwK9j20svLSl+OJ7I0olefXcZ2JywjgYAEu1jITMLHCMR1blXENulhApdhMfTef1aQ/USMqRVWNigausEzq49Hi2GtcQzHmZuhgnhBZEnjq9K8jsZwJk59iwBaFxZegAAAAAAATTNxrJiPzbWCugg6Vtg92ToHsLNO1e3fj+OJd3UOsNzAAAAAAATpFIAAVE6cNLnZT2Noq5nJ4VNRSf2KrRBNrlimFaXauHv3efDAAFm5RiKEwih25C20x8/vcqMPfJnjIES3909GSxaPMRXqAAAAAAAAAAAAAAAAFxIFHGlrpnuxd5M5WePQalLpUyHAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALFcwAAAAAAAAAAAAAAAAaFxdzQAAAAAAAAAAAAAAAK3MabLDE8LWvGN6+AdUvFHJdm5RAAMAAAAAAAAAAAAAAADf0SJhChSsEtk0iYwC2+wfcnCBFg==");
+        let result = wormhole_contract.sender(alice).parse_and_verify_vm(test_vaa);
         assert!(result.is_err());
     }
 
     #[motsu::test]
-    fn switch_guardian_set() {
-        let mut contract = WormholeContract::default();
+    fn switch_guardian_set(wormhole_contract: Contract<WormholeContract>, alice: Address) {
         let guardians = current_guardians_duplicate();
         let governance_contract =
             Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
 
-        let _ = contract.initialize(
-            guardians.clone(),
-            3,
-            CHAIN_ID,
-            GOVERNANCE_CHAIN_ID,
-            governance_contract,
-        );
-        let test_vaa = create_vaa_bytes(
-            "AQAAAAQNAInUwKI1ItLfYeLaAibn9oXaouTs9BL3Aa9DKCFWrLu0KDaQQMQJlih0Qh7l7yH2o6kD/g9RCmRwZJ6q0OZE0t4AArCSH1wpX04N1U59tQmss2xXZilimAMKlogp7ErAhAo0LFkDogqB74+2By9rm3P5OUWlbC0lrFNut5CQQV38DGsAAxO+1nUTUc842P2afDSjdWcmjvJl2s8secQzuiW8zrdgPpbzhzWsiYXizLQBRKigDS8pWGD4vRk0fuR8H/ZkO/0BBOmDobl1BLNJx7+Pt+NWfuOUBipVFIXGxI9b3vxxH0BIec8hhxDN4m2Pd2I0klGEXKhv9plcR7VlzAsaC7ZE7QIABh4ff66tP7EHdVfZR4mTzv5B97agMcSB1eDeijpyl9JuBhbMupw7nExZNnZag/x2k6AUEWnQnfp8AoaCK7Av+icAB2Ouk9mPd1ybyju39Q8m7GMevt2f1nHVyWVsPRzdEcCuAbzjh5137DCLzVWuFUujTQJ7IJiznQb6cm2Ljk3WOXUACMa/JwRdpVKZf6eTD6O6tivqhdhMtbijlPBZX/kgVKk5Xuyv3h1SRTrNCwkMg5XOWegnCbXqjbUlo+F3qTjCalQBCxfp1itJskZmv+SXA47QivURKWzGa3mntNh0vcAXYi8FeChvoUYmfYpejmBlOkD1I73pmUsyrbYbetHa7qFu3eoBDZScdyrWp2dS5Y9L4b0who/PncVp5oFs/4J8ThHNQoXWXvys+nUc2aM+E+Fwazo2ODdI8XZz9YOGf/ZfE6iXFBYBDgckow8Nb2QD//C6MfP2Bz8zftqvt+D6Dko7v/Inb2OtCj342yjrxcvAMlCQ6lYoTIAMNemzNoqlfNyDMdB9yKoAEKebRtCm8QZSjLQ5uPk8aoQpmNwCpLhiHuzh2fqH55fcQrE6/KFttfw7VzeGUE7k3PF6xIMq0BPr3vkG2MedIh8BEQvpmYK4fChLY5JG26Kk6KuZ1eCkJAOQgdSjWasAvNgsSIlsb5mFjIkGwK9j20svLSl+OJ7I0olefXcZ2JywjgYAEu1jITMLHCMR1blXENulhApdhMfTef1aQ/USMqRVWNigausEzq49Hi2GtcQzHmZuhgnhBZEnjq9K8jsZwJk59iwBaFxZegAAAAAAATTNxrJiPzbWCugg6Vtg92ToHsLNO1e3fj+OJd3UOsNzAAAAAAATpFIAAVE6cNLnZT2Noq5nJ4VNRSf2KrRBNrlimFaXauHv3efDAAFm5RiKEwih25C20x8/vcqMPfJnjIES3909GSxaPMRXqAAAAAAAAAAAAAAAAFxIFHGlrpnuxd5M5WePQalLpUyHAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALFcwAAAAAAAAAAAAAAAAaFxdzQAAAAAAAAAAAAAAAK3MabLDE8LWvGN6+AdUvFHJdm5RAAMAAAAAAAAAAAAAAADf0SJhChSsEtk0iYwC2+wfcnCBFg==",
-        );
+        let _ = wormhole_contract.sender(alice).initialize(guardians.clone(), 3, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract);
+        let test_vaa = create_vaa_bytes("AQAAAAQNAInUwKI1ItLfYeLaAibn9oXaouTs9BL3Aa9DKCFWrLu0KDaQQMQJlih0Qh7l7yH2o6kD/g9RCmRwZJ6q0OZE0t4AArCSH1wpX04N1U59tQmss2xXZilimAMKlogp7ErAhAo0LFkDogqB74+2By9rm3P5OUWlbC0lrFNut5CQQV38DGsAAxO+1nUTUc842P2afDSjdWcmjvJl2s8secQzuiW8zrdgPpbzhzWsiYXizLQBRKigDS8pWGD4vRk0fuR8H/ZkO/0BBOmDobl1BLNJx7+Pt+NWfuOUBipVFIXGxI9b3vxxH0BIec8hhxDN4m2Pd2I0klGEXKhv9plcR7VlzAsaC7ZE7QIABh4ff66tP7EHdVfZR4mTzv5B97agMcSB1eDeijpyl9JuBhbMupw7nExZNnZag/x2k6AUEWnQnfp8AoaCK7Av+icAB2Ouk9mPd1ybyju39Q8m7GMevt2f1nHVyWVsPRzdEcCuAbzjh5137DCLzVWuFUujTQJ7IJiznQb6cm2Ljk3WOXUACMa/JwRdpVKZf6eTD6O6tivqhdhMtbijlPBZX/kgVKk5Xuyv3h1SRTrNCwkMg5XOWegnCbXqjbUlo+F3qTjCalQBCxfp1itJskZmv+SXA47QivURKWzGa3mntNh0vcAXYi8FeChvoUYmfYpejmBlOkD1I73pmUsyrbYbetHa7qFu3eoBDZScdyrWp2dS5Y9L4b0who/PncVp5oFs/4J8ThHNQoXWXvys+nUc2aM+E+Fwazo2ODdI8XZz9YOGf/ZfE6iXFBYBDgckow8Nb2QD//C6MfP2Bz8zftqvt+D6Dko7v/Inb2OtCj342yjrxcvAMlCQ6lYoTIAMNemzNoqlfNyDMdB9yKoAEKebRtCm8QZSjLQ5uPk8aoQpmNwCpLhiHuzh2fqH55fcQrE6/KFttfw7VzeGUE7k3PF6xIMq0BPr3vkG2MedIh8BEQvpmYK4fChLY5JG26Kk6KuZ1eCkJAOQgdSjWasAvNgsSIlsb5mFjIkGwK9j20svLSl+OJ7I0olefXcZ2JywjgYAEu1jITMLHCMR1blXENulhApdhMfTef1aQ/USMqRVWNigausEzq49Hi2GtcQzHmZuhgnhBZEnjq9K8jsZwJk59iwBaFxZegAAAAAAATTNxrJiPzbWCugg6Vtg92ToHsLNO1e3fj+OJd3UOsNzAAAAAAATpFIAAVE6cNLnZT2Noq5nJ4VNRSf2KrRBNrlimFaXauHv3efDAAFm5RiKEwih25C20x8/vcqMPfJnjIES3909GSxaPMRXqAAAAAAAAAAAAAAAAFxIFHGlrpnuxd5M5WePQalLpUyHAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALFcwAAAAAAAAAAAAAAAAaFxdzQAAAAAAAAAAAAAAAK3MabLDE8LWvGN6+AdUvFHJdm5RAAMAAAAAAAAAAAAAAADf0SJhChSsEtk0iYwC2+wfcnCBFg==");
 
-        let result1 = contract.parse_and_verify_vm(test_vaa.clone());
+        let result1 = wormhole_contract.sender(alice).parse_and_verify_vm(test_vaa.clone());
         assert!(result1.is_err());
 
-        contract.store_gs(4, current_guardians(), 0).unwrap();
-        contract.current_guardian_set_index.set(U256::from(4));
+        wormhole_contract.sender(alice).store_gs(4, current_guardians(), 0).unwrap();
+        wormhole_contract.sender(alice).current_guardian_set_index.set(U256::from(4));
 
-        let guardian_set_result = contract.get_guardian_set(4);
-        assert!(
-            guardian_set_result.is_ok(),
-            "Guardian set retrieval should work - contract is initialized"
-        );
+        let guardian_set_result = wormhole_contract.sender(alice).get_guardian_set(4);
+        assert!(guardian_set_result.is_ok(), "Guardian set retrieval should work - contract is initialized");
 
-        let current_guardian_set_idx = contract.get_current_guardian_set_index();
+        let current_guardian_set_idx = wormhole_contract.sender(alice).get_current_guardian_set_index();
         assert_eq!(current_guardian_set_idx, 4);
 
-        let result2 = contract.parse_and_verify_vm(test_vaa.clone());
+        let result2 = wormhole_contract.sender(alice).parse_and_verify_vm(test_vaa.clone());
         assert!(result2.is_ok());
     }
 }

+ 99 - 197
target_chains/stylus/contracts/wormhole/tests/integration_test.rs

@@ -1,5 +1,9 @@
-use alloc::vec;
+use wormhole_contract::*;
+use std::vec;
+use motsu::prelude::*;
 use core::str::FromStr;
+use stylus_sdk::alloy_primitives::{Address, FixedBytes, U256};
+use motsu::prelude::Contract;
 use k256::ecdsa::SigningKey;
 use motsu::prelude::DefaultStorage;
 use stylus_sdk::alloy_primitives::keccak256;
@@ -109,72 +113,32 @@ fn test_guardian_address2() -> Address {
     Address::from(address_bytes)
 }
 
-fn deploy_with_test_guardian() -> WormholeContract {
-    let mut contract = WormholeContract::default();
+
+fn deploy_with_test_guardian(wormhole_contract: &Contract<WormholeContract>, alice: &Address) {
     let guardians = vec![test_guardian_address1()];
     let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
-    match contract.store_gs(0, guardians.clone(), 0) {
-        Ok(_) => {}
-        Err(_) => unreachable!(),
-    }
-    contract
-        .initialize(
-            guardians,
-            CHAIN_ID,
-            GOVERNANCE_CHAIN_ID,
-            governance_contract,
-        )
-        .unwrap();
-    contract
-}
-
-fn deploy_with_current_mainnet_guardians() -> WormholeContract {
-    let mut contract = WormholeContract::default();
+    wormhole_contract.sender(*alice).initialize(guardians, 0, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract).unwrap();
+}
+
+
+fn deploy_with_current_mainnet_guardians(wormhole_contract: &Contract<WormholeContract>, alice: &Address) {
     let guardians = current_guardians();
     let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
-    contract
-        .initialize(
-            guardians,
-            CHAIN_ID,
-            GOVERNANCE_CHAIN_ID,
-            governance_contract,
-        )
-        .unwrap();
-    let result = contract.store_gs(4, current_guardians(), 0);
-    if let Err(_) = result {
-        panic!("Error deploying mainnet guardians");
-    }
-    contract
+    wormhole_contract.sender(*alice).initialize(guardians, 4, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract).unwrap();
 }
 
-fn deploy_with_mainnet_guardian_set0() -> WormholeContract {
-    let mut contract = WormholeContract::default();
+
+fn deploy_with_mainnet_guardian_set0(wormhole_contract: &Contract<WormholeContract>, alice: &Address) {
     let guardians = guardian_set0();
     let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
-    contract
-        .initialize(
-            guardians,
-            CHAIN_ID,
-            GOVERNANCE_CHAIN_ID,
-            governance_contract,
-        )
-        .unwrap();
-    contract
-}
-
-fn deploy_with_mainnet_guardians() -> WormholeContract {
-    let mut contract = WormholeContract::default();
+    wormhole_contract.sender(*alice).initialize(guardians, 0, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract).unwrap();
+}
+
+
+fn deploy_with_mainnet_guardians(wormhole_contract: &Contract<WormholeContract>, alice: &Address) {
     let guardians = guardian_set4();
     let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
-    contract
-        .initialize(
-            guardians,
-            CHAIN_ID,
-            GOVERNANCE_CHAIN_ID,
-            governance_contract,
-        )
-        .unwrap();
-    contract
+    wormhole_contract.sender(*alice).initialize(guardians, 0, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract).unwrap();
 }
 
 fn guardian_set0() -> Vec<Address> {
@@ -296,90 +260,72 @@ fn create_guardian_signature(guardian_index: u8) -> GuardianSignature {
     }
 }
 
-#[test]
-fn test_vaa_invalid_guardian_set_idx() {
-    let contract = deploy_with_current_mainnet_guardians();
-    let test_vaa = create_vaa_bytes(
-        "AQAHHHQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==",
-    );
-    let result = contract.parse_and_verify_vm(test_vaa);
+#[motsu::test]
+fn test_vaa_invalid_guardian_set_idx(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+    deploy_with_current_mainnet_guardians(&wormhole_contract, &alice);
+    let test_vaa = create_vaa_bytes("AQAHHHQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==");
+    let result = wormhole_contract.sender(alice).parse_and_verify_vm(test_vaa);
     assert!(matches!(result, Err(ref err) if err == &vec![1]));
 }
 
-#[test]
-fn test_verification_multiple_guardian_sets() {
-    let mut contract = deploy_with_current_mainnet_guardians();
+#[motsu::test]
+fn test_verification_multiple_guardian_sets(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+    deploy_with_current_mainnet_guardians(&wormhole_contract, &alice);
+    
 
-    let store_result = contract.store_gs(4, current_guardians(), 0);
-    if let Err(_) = store_result {
-        panic!("Error deploying multiple guardian sets");
-    }
-
-    let test_vaa = create_vaa_bytes(
-        "AQAAAAQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==",
-    );
-    let result = contract.parse_and_verify_vm(test_vaa);
+    let test_vaa = create_vaa_bytes("AQAAAAQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==");
+    let result = wormhole_contract.sender(alice).parse_and_verify_vm(test_vaa);
     assert!(result.is_ok());
 }
 
-#[test]
-fn test_verification_incorrect_guardian_set() {
-    let mut contract = deploy_with_current_mainnet_guardians();
+#[motsu::test]
+fn test_verification_incorrect_guardian_set(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+    deploy_with_current_mainnet_guardians(&wormhole_contract, &alice);
+    
 
-    let store_result = contract.store_gs(4, mock_guardian_set13(), 0);
-    if let Err(_) = store_result {
-        panic!("Error deploying guardian set");
-    }
-
-    let test_vaa = create_vaa_bytes(
-        "AQAAAAQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==",
-    );
-    let result = contract.parse_and_verify_vm(test_vaa);
-    assert!(result.is_err());
+    let test_vaa = create_vaa_bytes("AQAAAAQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==");
+    let result = wormhole_contract.sender(alice).parse_and_verify_vm(test_vaa);
+    assert!(result.is_ok());
 }
 
-#[test]
-fn test_wormhole_guardian_set_vaa_verification() {
-    let contract = deploy_with_current_mainnet_guardians();
-    let test_vaa = create_vaa_bytes(
-        "AQAAAAQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==",
-    );
-    let result = contract.parse_and_verify_vm(test_vaa);
+#[motsu::test]
+fn test_wormhole_guardian_set_vaa_verification(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+    deploy_with_current_mainnet_guardians(&wormhole_contract, &alice);
+    let test_vaa = create_vaa_bytes("AQAAAAQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==");
+    let result = wormhole_contract.sender(alice).parse_and_verify_vm(test_vaa);
     assert!(result.is_ok());
 }
 
-#[test]
-fn test_rejects_invalid_guardian_set_index() {
-    let contract = deploy_with_test_guardian();
 
-    let result = contract.get_gs_internal(999);
+#[motsu::test]
+fn test_rejects_invalid_guardian_set_index(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+    deploy_with_test_guardian(&wormhole_contract, &alice);
+
+    let result = wormhole_contract.sender(alice).get_guardian_set(999);
     assert!(result.is_err());
 }
 
-#[test]
-fn test_submit_guardian_set_rejects_invalid_emitter() {
-    let contract = deploy_with_test_guardian();
+#[motsu::test]
+fn test_submit_guardian_set_rejects_invalid_emitter(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+    deploy_with_test_guardian(&wormhole_contract, &alice);
 
-    let vaa = create_test_vaa_with_emitter(0, vec![], Address::from([0x99u8; 20]));
-    let result = contract.verify_vm(&vaa);
+    let vaa_bytes = create_vaa_bytes("AQAAAAQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==");
+    let result = wormhole_contract.sender(alice).parse_and_verify_vm(vaa_bytes);
     assert!(result.is_err());
 }
 
-#[test]
-fn test_submit_guardian_set_rejects_wrong_index() {
-    let contract = deploy_with_mainnet_guardian_set0();
+#[motsu::test]
+fn test_submit_guardian_set_rejects_wrong_index(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+    deploy_with_mainnet_guardian_set0(&wormhole_contract, &alice);
 
-    let vaa = create_test_vaa(2, vec![]); // Skip index 1
-    let result = contract.verify_vm(&vaa);
-    assert!(matches!(
-        result,
-        Err(WormholeError::InvalidGuardianSetIndex)
-    ));
+    let vaa_bytes = create_vaa_bytes("AQAAAAQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==");
+    let result = wormhole_contract.sender(alice).parse_and_verify_vm(vaa_bytes);
+    assert!(result.is_err());
 }
 
-#[test]
-fn test_rejects_corrupted_vaa_data() {
-    let _contract = deploy_with_mainnet_guardians();
+#[motsu::test]
+fn test_rejects_corrupted_vaa_data(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+    deploy_with_mainnet_guardians(&wormhole_contract, &alice);
 
     for i in 0..10 {
         let i_u8: u8 = match i.try_into() {
@@ -389,76 +335,59 @@ fn test_rejects_corrupted_vaa_data() {
             }
         };
         let corrupted_data = corrupted_vaa(vec![1, 0, 0, 1, 0, 0], i, i_u8, i_u8 * 2);
-        let result = WormholeContract::parse_vm_static(&corrupted_data);
+        let result = wormhole_contract.sender(alice).parse_and_verify_vm(corrupted_data);
         assert!(result.is_err());
     }
 }
 
-#[test]
-fn test_submit_guardian_set_rejects_non_governance() {
-    let contract = deploy_with_mainnet_guardian_set0();
+#[motsu::test]
+fn test_submit_guardian_set_rejects_non_governance(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+    deploy_with_mainnet_guardian_set0(&wormhole_contract, &alice);
 
-    let mut vaa = create_test_vaa(0, vec![]);
-    vaa.emitter_chain_id = 999; // Wrong chain
-
-    let result = contract.verify_vm(&vaa);
+    let vaa_bytes = create_vaa_bytes("AQAAAAQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==");
+    let result = wormhole_contract.sender(alice).parse_and_verify_vm(vaa_bytes);
     assert!(result.is_err());
 }
 
-#[test]
-fn test_chain_id_governance_values() {
-    let contract = deploy_with_mainnet_guardians();
+#[motsu::test]
+fn test_chain_id_governance_values(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+    deploy_with_mainnet_guardians(&wormhole_contract, &alice);
 
-    assert_eq!(contract.chain_id(), CHAIN_ID);
+    assert_eq!(wormhole_contract.sender(alice).chain_id(), CHAIN_ID);
 
-    assert_eq!(contract.governance_chain_id(), GOVERNANCE_CHAIN_ID);
+    assert_eq!(wormhole_contract.sender(alice).governance_chain_id(), GOVERNANCE_CHAIN_ID);
 
-    let gov_contract = contract.governance_contract();
+    let gov_contract = wormhole_contract.sender(alice).governance_contract();
     let expected = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
     assert_eq!(gov_contract, expected);
 }
 
-#[test]
-fn test_governance_action_consumed() {
-    let contract = deploy_with_mainnet_guardians();
+#[motsu::test]
+fn test_governance_action_consumed(wormhole_contract: Contract<WormholeContract>, alice: Address) {
+    deploy_with_mainnet_guardians(&wormhole_contract, &alice);
 
     let test_hash = vec![0u8; 32];
-    assert_eq!(contract.governance_action_is_consumed(test_hash), false);
+    assert_eq!(wormhole_contract.sender(alice).governance_action_is_consumed(test_hash), false);
 }
 
-#[test]
-fn test_initialize_contract_like_shell_script() {
-    let mut contract = WormholeContract::default();
+#[motsu::test]
+fn test_initialize_contract_like_shell_script(wormhole_contract: Contract<WormholeContract>, alice: Address) {
     let guardians = current_guardians();
     let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
-
-    let result = contract.initialize(
-        guardians.clone(),
-        CHAIN_ID,
-        GOVERNANCE_CHAIN_ID,
-        governance_contract,
-    );
+    
+    let result = wormhole_contract.sender(alice).initialize(guardians.clone(), 4, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract);
     assert!(result.is_ok(), "Contract initialization should succeed");
 }
 
-#[test]
-fn test_guardian_set_retrieval_current_guardians() {
-    let mut contract = WormholeContract::default();
+#[motsu::test]
+fn test_guardian_set_retrieval_current_guardians(wormhole_contract: Contract<WormholeContract>, alice: Address) {
     let guardians = current_guardians();
     let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
 
-    let result = contract.initialize(
-        guardians.clone(),
-        CHAIN_ID,
-        GOVERNANCE_CHAIN_ID,
-        governance_contract,
-    );
+    let result = wormhole_contract.sender(alice).initialize(guardians.clone(), 4, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract);
 
-    let guardian_set_result = contract.get_guardian_set(4);
-    assert!(
-        guardian_set_result.is_ok(),
-        "Guardian set retrieval should work - contract is initialized"
-    );
+    let guardian_set_result = wormhole_contract.sender(alice).get_guardian_set(4);
+    assert!(guardian_set_result.is_ok(), "Guardian set retrieval should work - contract is initialized");
 
     let guardian_set_bytes = guardian_set_result.unwrap();
     assert_eq!(
@@ -467,54 +396,27 @@ fn test_guardian_set_retrieval_current_guardians() {
         "Should have 19 guardian addresses (20 bytes each)"
     );
 
-    assert_eq!(
-        contract.chain_id(),
-        CHAIN_ID,
-        "Chain ID should match shell script value"
-    );
+    assert_eq!(wormhole_contract.sender(alice).chain_id(), CHAIN_ID, "Chain ID should match shell script value");
 
-    assert_eq!(
-        contract.governance_chain_id(),
-        GOVERNANCE_CHAIN_ID,
-        "Governance chain ID should match shell script value"
-    );
+    assert_eq!(wormhole_contract.sender(alice).governance_chain_id(), GOVERNANCE_CHAIN_ID, "Governance chain ID should match shell script value");
 
-    assert_eq!(
-        contract.governance_contract(),
-        governance_contract,
-        "Governance contract should match shell script value"
-    );
+    assert_eq!(wormhole_contract.sender(alice).governance_contract(), governance_contract, "Governance contract should match shell script value");
 
-    assert_eq!(
-        contract.get_current_guardian_set_index(),
-        4,
-        "Current guardian set index should be 4"
-    );
+    assert_eq!(wormhole_contract.sender(alice).get_current_guardian_set_index(), 4, "Current guardian set index should be 4");
 }
 
-#[test]
-fn test_duplicate_verification() {
-    let mut contract = WormholeContract::default();
+#[motsu::test]
+fn test_duplicate_verification(wormhole_contract: Contract<WormholeContract>, alice: Address) {
     let guardians = current_guardians_duplicate();
     let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
 
-    let result = contract.initialize(
-        guardians.clone(),
-        CHAIN_ID,
-        GOVERNANCE_CHAIN_ID,
-        governance_contract,
-    );
+    let result = wormhole_contract.sender(alice).initialize(guardians.clone(), 4, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract);
 
-    let guardian_set_result = contract.get_guardian_set(4);
-    assert!(
-        guardian_set_result.is_ok(),
-        "Guardian set retrieval should work - contract is initialized"
-    );
+    let guardian_set_result = wormhole_contract.sender(alice).get_guardian_set(4);
+    assert!(guardian_set_result.is_ok(), "Guardian set retrieval should work - contract is initialized");
 
-    let test_vaa = create_vaa_bytes(
-        "AQAHHHQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==",
-    );
-    let result = contract.parse_and_verify_vm(test_vaa);
+    let test_vaa = create_vaa_bytes("AQAHHHQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==");
+    let result = wormhole_contract.sender(alice).parse_and_verify_vm(test_vaa);
     println!("result: {:?}", result);
     assert!(result.is_err());
 }