Repository: OpenZeppelin/rust-contracts-stylus (rust)

Issue:
[Feature]: Aggregate dependencies in root Cargo.toml
### What is the feature you would like to see?

We are currently specifying most dependencies of member crates directly, but we should instead define the dependency in the root Cargo.toml, and then do `DEPENDENCY_NAME.workspace = true` in member crates.

The rationale is that this way, we ensure we're using a single version of a dependency.

This may not make sense for all dependencies, so this should be judge on a case-by-case basis.

### Contribution Guidelines

- [X] I agree to follow this project's Contribution Guidelines

Pull request:
ref: aggregate deps in root manifest
<!--
Thank you for your interest in contributing to OpenZeppelin!

Consider opening an issue for discussion prior to submitting a PR. New features will be merged faster if they were first discussed and designed with the team.

Describe the changes introduced in this pull request. Include any context necessary for understanding the PR's purpose.
-->
Moved all dependency importation to the root workspace `Cargo.toml` and referenced it at the crate level.

<!-- Fill in with issue number -->
Resolves #144 

#### PR Checklist

<!--
Before merging the pull request all of the following must be completed.
Feel free to submit a PR or Draft PR even if some items are pending.
Some of the items may not apply.
-->

- [ ] Tests
- [ ] Documentation


Patch:
diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml
index 84e1ee6..1164b11 100644
--- a/.github/workflows/check.yml
+++ b/.github/workflows/check.yml
@@ -106,4 +106,4 @@ jobs:
       - name: Checkout Actions Repository
         uses: actions/checkout@v4
       - name: Check spelling of files in the workspace
-        uses: crate-ci/typos@v1.22.7
+        uses: crate-ci/typos@v1.22.9
diff --git a/Cargo.toml b/Cargo.toml
index 7c461c1..237af86 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -47,6 +47,7 @@ pedantic = "warn"
 all = "warn"
 
 [workspace.dependencies]
+# stylus-related
 stylus-sdk = { version = "0.5.0", default-features = false }
 stylus-proc = { version = "0.5.0", default-features = false }
 mini-alloc = "0.4.2"
@@ -67,6 +68,21 @@ alloy = { version = "0.1.1", features = [
 alloy-primitives = { version = "0.3.1", default-features = false }
 alloy-sol-types = { version = "0.3.1", default-features = false }
 
+cfg-if = "1.0"
+const-hex = { version = "1.11.1", default-features = false }
+eyre = "0.6.8"
+koba = "0.1.0"
+once_cell = "1.19.0"
+rand = "0.8.5"
+regex = "1.10.4"
+tiny-keccak = { version = "2.0.2", features = ["keccak"] }
+tokio = { version = "1.12.0", features = ["full"] }
+
+# procedural macros
+syn = { version = "2.0.58", features = ["full"] }
+proc-macro2 = "1.0.79"
+quote = "1.0.35"
+
 [profile.release]
 codegen-units = 1
 panic = "abort"
diff --git a/contracts/Cargo.toml b/contracts/Cargo.toml
index 5e4a96e..e4b5bcb 100644
--- a/contracts/Cargo.toml
+++ b/contracts/Cargo.toml
@@ -14,12 +14,12 @@ alloy-sol-types.workspace = true
 stylus-sdk.workspace = true
 stylus-proc.workspace = true
 mini-alloc.workspace = true
-cfg-if = "1.0"
+cfg-if.workspace = true
 
 [dev-dependencies]
-alloy-primitives = { version = "0.3.1", features = ["arbitrary"] }
+alloy-primitives = { workspace = true, features = ["arbitrary"] }
 motsu = { path = "../lib/motsu" }
-rand = "0.8.5"
+rand.workspace = true
 
 [features]
 default = []
diff --git a/examples/erc20/Cargo.toml b/examples/erc20/Cargo.toml
index a1cc559..392437c 100644
--- a/examples/erc20/Cargo.toml
+++ b/examples/erc20/Cargo.toml
@@ -21,8 +21,8 @@ mini-alloc.workspace = true
 [dev-dependencies]
 alloy.workspace = true
 e2e = { path = "../../lib/e2e" }
-tokio = { version = "1.12.0", features = ["full"] }
-eyre = "0.6.8"
+tokio.workspace = true
+eyre.workspace = true
 
 [features]
 default = []
diff --git a/examples/erc721/Cargo.toml b/examples/erc721/Cargo.toml
index 284ecd9..79ef28e 100644
--- a/examples/erc721/Cargo.toml
+++ b/examples/erc721/Cargo.toml
@@ -22,9 +22,9 @@ mini-alloc.workspace = true
 [dev-dependencies]
 alloy.workspace = true
 e2e = { path = "../../lib/e2e" }
-tokio = { version = "1.12.0", features = ["full"] }
-eyre = "0.6.8"
-rand = "0.8.5"
+tokio.workspace = true
+eyre.workspace = true
+rand.workspace = true
 
 [features]
 default = []
diff --git a/examples/ownable/Cargo.toml b/examples/ownable/Cargo.toml
index d698503..97e8415 100644
--- a/examples/ownable/Cargo.toml
+++ b/examples/ownable/Cargo.toml
@@ -19,8 +19,8 @@ mini-alloc.workspace = true
 [dev-dependencies]
 alloy.workspace = true
 e2e = { path = "../../lib/e2e" }
-tokio = { version = "1.12.0", features = ["full"] }
-eyre = "0.6.8"
+tokio.workspace = true
+eyre.workspace = true
 
 [lib]
 crate-type = ["lib", "cdylib"]
diff --git a/lib/crypto/Cargo.toml b/lib/crypto/Cargo.toml
index 8db71cc..9bfe257 100644
--- a/lib/crypto/Cargo.toml
+++ b/lib/crypto/Cargo.toml
@@ -10,11 +10,11 @@ version = "0.1.0"
 
 [dependencies]
 mini-alloc.workspace = true
-tiny-keccak = { version = "2.0.2", features = ["keccak"] }
+tiny-keccak.workspace = true
 
 [dev-dependencies]
 hex-literal = "0.4.1"
-rand = "0.8.5"
+rand.workspace = true
 
 [features]
 std = []
diff --git a/lib/motsu-proc/Cargo.toml b/lib/motsu-proc/Cargo.toml
index 2916ab3..73e1548 100644
--- a/lib/motsu-proc/Cargo.toml
+++ b/lib/motsu-proc/Cargo.toml
@@ -8,9 +8,9 @@ repository.workspace = true
 version = "0.1.0"
 
 [dependencies]
-proc-macro2 = "1.0.79"
-quote = "1.0.35"
-syn = { version = "2.0.58", features = ["full"] }
+proc-macro2.workspace = true
+quote.workspace = true
+syn.workspace = true
 
 [dev-dependencies]
 motsu = { path = "../motsu" }
diff --git a/lib/motsu/Cargo.toml b/lib/motsu/Cargo.toml
index 9ac16dc..5cbb1e3 100644
--- a/lib/motsu/Cargo.toml
+++ b/lib/motsu/Cargo.toml
@@ -8,9 +8,9 @@ repository.workspace = true
 version = "0.1.0"
 
 [dependencies]
-const-hex = { version = "1.11.1", default-features = false }
-once_cell = { version = "1.19.0" }
-tiny-keccak = { version = "2.0.2", features = ["keccak"] }
+const-hex.workspace = true
+once_cell.workspace = true
+tiny-keccak.workspace = true
 motsu-proc = { path = "../motsu-proc" }
 
 [lints]


Test patch:
diff --git a/examples/erc20/tests/erc20.rs b/examples/erc20/tests/erc20.rs
index c7e73d2..059c94f 100644
--- a/examples/erc20/tests/erc20.rs
+++ b/examples/erc20/tests/erc20.rs
@@ -6,7 +6,7 @@ use alloy::{
     sol_types::{SolConstructor, SolError},
 };
 use alloy_primitives::uint;
-use e2e::{receipt, send, watch, EventExt, Panic, PanicCode, Revert, User};
+use e2e::{receipt, send, watch, Account, EventExt, Panic, PanicCode, Revert};
 use eyre::Result;
 
 use crate::abi::Erc20;
@@ -38,7 +38,7 @@ async fn deploy(
 // ============================================================================
 
 #[e2e::test]
-async fn constructs(alice: User) -> Result<()> {
+async fn constructs(alice: Account) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk(), None).await?;
     let contract = Erc20::new(contract_addr, &alice.wallet);
 
@@ -58,7 +58,7 @@ async fn constructs(alice: User) -> Result<()> {
 }
 
 #[e2e::test]
-async fn mints(alice: User) -> Result<()> {
+async fn mints(alice: Account) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk(), None).await?;
     let contract = Erc20::new(contract_addr, &alice.wallet);
     let alice_addr = alice.address();
@@ -90,7 +90,7 @@ async fn mints(alice: User) -> Result<()> {
 }
 
 #[e2e::test]
-async fn mints_rejects_invalid_receiver(alice: User) -> Result<()> {
+async fn mints_rejects_invalid_receiver(alice: Account) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk(), None).await?;
     let contract = Erc20::new(contract_addr, &alice.wallet);
     let invalid_receiver = Address::ZERO;
@@ -118,7 +118,7 @@ async fn mints_rejects_invalid_receiver(alice: User) -> Result<()> {
 }
 
 #[e2e::test]
-async fn mints_rejects_overflow(alice: User) -> Result<()> {
+async fn mints_rejects_overflow(alice: Account) -> Result<()> {
     let max_cap = U256::MAX;
 
     let contract_addr = deploy(alice.url(), &alice.pk(), Some(max_cap)).await?;
@@ -153,7 +153,7 @@ async fn mints_rejects_overflow(alice: User) -> Result<()> {
 }
 
 #[e2e::test]
-async fn transfers(alice: User, bob: User) -> Result<()> {
+async fn transfers(alice: Account, bob: Account) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk(), None).await?;
     let contract_alice = Erc20::new(contract_addr, &alice.wallet);
     let alice_addr = alice.address();
@@ -191,8 +191,8 @@ async fn transfers(alice: User, bob: User) -> Result<()> {
 
 #[e2e::test]
 async fn transfer_rejects_insufficient_balance(
-    alice: User,
-    bob: User,
+    alice: Account,
+    bob: Account,
 ) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk(), None).await?;
     let contract_alice = Erc20::new(contract_addr, &alice.wallet);
@@ -234,7 +234,7 @@ async fn transfer_rejects_insufficient_balance(
 }
 
 #[e2e::test]
-async fn transfer_rejects_invalid_receiver(alice: User) -> Result<()> {
+async fn transfer_rejects_invalid_receiver(alice: Account) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk(), None).await?;
     let contract_alice = Erc20::new(contract_addr, &alice.wallet);
     let alice_addr = alice.address();
@@ -273,7 +273,7 @@ async fn transfer_rejects_invalid_receiver(alice: User) -> Result<()> {
 }
 
 #[e2e::test]
-async fn approves(alice: User, bob: User) -> Result<()> {
+async fn approves(alice: Account, bob: Account) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk(), None).await?;
     let contract = Erc20::new(contract_addr, &alice.wallet);
     let alice_addr = alice.address();
@@ -340,7 +340,7 @@ async fn approves(alice: User, bob: User) -> Result<()> {
 }
 
 #[e2e::test]
-async fn approve_rejects_invalid_spender(alice: User) -> Result<()> {
+async fn approve_rejects_invalid_spender(alice: Account) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk(), None).await?;
     let contract = Erc20::new(contract_addr, &alice.wallet);
     let alice_addr = alice.address();
@@ -390,7 +390,7 @@ async fn approve_rejects_invalid_spender(alice: User) -> Result<()> {
 }
 
 #[e2e::test]
-async fn transfers_from(alice: User, bob: User) -> Result<()> {
+async fn transfers_from(alice: Account, bob: Account) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk(), None).await?;
     let contract_alice = Erc20::new(contract_addr, &alice.wallet);
     let contract_bob = Erc20::new(contract_addr, &bob.wallet);
@@ -439,8 +439,8 @@ async fn transfers_from(alice: User, bob: User) -> Result<()> {
 
 #[e2e::test]
 async fn transfer_from_reverts_insufficient_balance(
-    alice: User,
-    bob: User,
+    alice: Account,
+    bob: Account,
 ) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk(), None).await?;
     let contract_alice = Erc20::new(contract_addr, &alice.wallet);
@@ -494,8 +494,8 @@ async fn transfer_from_reverts_insufficient_balance(
 
 #[e2e::test]
 async fn transfer_from_rejects_insufficient_allowance(
-    alice: User,
-    bob: User,
+    alice: Account,
+    bob: Account,
 ) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk(), None).await?;
     let contract_alice = Erc20::new(contract_addr, &alice.wallet);
@@ -549,8 +549,8 @@ async fn transfer_from_rejects_insufficient_allowance(
 
 #[e2e::test]
 async fn transfer_from_rejects_invalid_receiver(
-    alice: User,
-    bob: User,
+    alice: Account,
+    bob: Account,
 ) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk(), None).await?;
     let contract_alice = Erc20::new(contract_addr, &alice.wallet);
@@ -607,7 +607,7 @@ async fn transfer_from_rejects_invalid_receiver(
 // ============================================================================
 
 #[e2e::test]
-async fn burns(alice: User) -> Result<()> {
+async fn burns(alice: Account) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk(), None).await?;
     let contract_alice = Erc20::new(contract_addr, &alice.wallet);
     let alice_addr = alice.address();
@@ -642,7 +642,7 @@ async fn burns(alice: User) -> Result<()> {
 }
 
 #[e2e::test]
-async fn burn_rejects_insufficient_balance(alice: User) -> Result<()> {
+async fn burn_rejects_insufficient_balance(alice: Account) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk(), None).await?;
     let contract_alice = Erc20::new(contract_addr, &alice.wallet);
     let alice_addr = alice.address();
@@ -677,7 +677,7 @@ async fn burn_rejects_insufficient_balance(alice: User) -> Result<()> {
 }
 
 #[e2e::test]
-async fn burns_from(alice: User, bob: User) -> Result<()> {
+async fn burns_from(alice: Account, bob: Account) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk(), None).await?;
     let contract_alice = Erc20::new(contract_addr, &alice.wallet);
     let contract_bob = Erc20::new(contract_addr, &bob.wallet);
@@ -729,8 +729,8 @@ async fn burns_from(alice: User, bob: User) -> Result<()> {
 
 #[e2e::test]
 async fn burn_from_reverts_insufficient_balance(
-    alice: User,
-    bob: User,
+    alice: Account,
+    bob: Account,
 ) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk(), None).await?;
     let contract_alice = Erc20::new(contract_addr, &alice.wallet);
@@ -784,8 +784,8 @@ async fn burn_from_reverts_insufficient_balance(
 
 #[e2e::test]
 async fn burn_from_rejects_insufficient_allowance(
-    alice: User,
-    bob: User,
+    alice: Account,
+    bob: Account,
 ) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk(), None).await?;
     let contract_alice = Erc20::new(contract_addr, &alice.wallet);
@@ -842,7 +842,7 @@ async fn burn_from_rejects_insufficient_allowance(
 // ============================================================================
 
 #[e2e::test]
-async fn mint_rejects_exceeding_cap(alice: User) -> Result<()> {
+async fn mint_rejects_exceeding_cap(alice: Account) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk(), None).await?;
     let contract_alice = Erc20::new(contract_addr, &alice.wallet);
     let alice_addr = alice.address();
@@ -878,7 +878,7 @@ async fn mint_rejects_exceeding_cap(alice: User) -> Result<()> {
 }
 
 #[e2e::test]
-async fn mint_rejects_when_cap_reached(alice: User) -> Result<()> {
+async fn mint_rejects_when_cap_reached(alice: Account) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk(), None).await?;
     let contract_alice = Erc20::new(contract_addr, &alice.wallet);
     let alice_addr = alice.address();
@@ -913,7 +913,9 @@ async fn mint_rejects_when_cap_reached(alice: User) -> Result<()> {
 }
 
 #[e2e::test]
-async fn should_not_deploy_capped_with_invalid_cap(alice: User) -> Result<()> {
+async fn should_not_deploy_capped_with_invalid_cap(
+    alice: Account,
+) -> Result<()> {
     let invalid_cap = U256::ZERO;
     let err = deploy(alice.url(), &alice.pk(), Some(invalid_cap))
         .await
diff --git a/examples/erc721/tests/erc721.rs b/examples/erc721/tests/erc721.rs
index 273022c..e78f9ee 100644
--- a/examples/erc721/tests/erc721.rs
+++ b/examples/erc721/tests/erc721.rs
@@ -6,7 +6,7 @@ use alloy::{
     sol_types::SolConstructor,
 };
 use alloy_primitives::uint;
-use e2e::{receipt, send, watch, EventExt, Revert, User};
+use e2e::{receipt, send, watch, Account, EventExt, Revert};
 
 use crate::abi::Erc721;
 
@@ -32,7 +32,7 @@ async fn deploy(rpc_url: &str, private_key: &str) -> eyre::Result<Address> {
 }
 
 #[e2e::test]
-async fn constructs(alice: User) -> eyre::Result<()> {
+async fn constructs(alice: Account) -> eyre::Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk()).await?;
     let contract = Erc721::new(contract_addr, &alice.wallet);
 
@@ -45,7 +45,7 @@ async fn constructs(alice: User) -> eyre::Result<()> {
 }
 
 #[e2e::test]
-async fn mints(alice: User) -> eyre::Result<()> {
+async fn mints(alice: Account) -> eyre::Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk()).await?;
     let contract = Erc721::new(contract_addr, &alice.wallet);
 
@@ -61,7 +61,7 @@ async fn mints(alice: User) -> eyre::Result<()> {
 }
 
 #[e2e::test]
-async fn errors_when_reusing_token_id(alice: User) -> eyre::Result<()> {
+async fn errors_when_reusing_token_id(alice: Account) -> eyre::Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk()).await?;
     let contract = Erc721::new(contract_addr, &alice.wallet);
 
@@ -77,7 +77,7 @@ async fn errors_when_reusing_token_id(alice: User) -> eyre::Result<()> {
 }
 
 #[e2e::test]
-async fn transfers(alice: User, bob: User) -> eyre::Result<()> {
+async fn transfers(alice: Account, bob: Account) -> eyre::Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk()).await?;
     let contract = Erc721::new(contract_addr, &alice.wallet);
 
@@ -102,8 +102,8 @@ async fn transfers(alice: User, bob: User) -> eyre::Result<()> {
 
 #[e2e::test]
 async fn errors_when_transfer_nonexistent_token(
-    alice: User,
-    bob: User,
+    alice: Account,
+    bob: Account,
 ) -> eyre::Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk()).await?;
     let contract = Erc721::new(contract_addr, &alice.wallet);
@@ -120,7 +120,10 @@ async fn errors_when_transfer_nonexistent_token(
 }
 
 #[e2e::test]
-async fn approves_token_transfer(alice: User, bob: User) -> eyre::Result<()> {
+async fn approves_token_transfer(
+    alice: Account,
+    bob: Account,
+) -> eyre::Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk()).await?;
     let contract = Erc721::new(contract_addr, &alice.wallet);
 
@@ -141,8 +144,8 @@ async fn approves_token_transfer(alice: User, bob: User) -> eyre::Result<()> {
 
 #[e2e::test]
 async fn errors_when_transfer_unapproved_token(
-    alice: User,
-    bob: User,
+    alice: Account,
+    bob: Account,
 ) -> eyre::Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk()).await?;
     let contract = Erc721::new(contract_addr, &alice.wallet);
diff --git a/examples/ownable/tests/ownable.rs b/examples/ownable/tests/ownable.rs
index 52fc03a..207d701 100644
--- a/examples/ownable/tests/ownable.rs
+++ b/examples/ownable/tests/ownable.rs
@@ -8,7 +8,7 @@ use alloy::{
     sol,
     sol_types::{SolConstructor, SolError, SolEvent},
 };
-use e2e::{receipt, send, EventExt, Revert, User};
+use e2e::{receipt, send, Account, EventExt, Revert};
 use eyre::Result;
 
 use crate::abi::Ownable;
@@ -17,10 +17,10 @@ mod abi;
 
 sol!("src/constructor.sol");
 
-async fn deploy(user: &User, owner: Address) -> eyre::Result<Address> {
+async fn deploy(account: &Account, owner: Address) -> eyre::Result<Address> {
     let args = OwnableExample::constructorCall { initialOwner: owner };
     let args = alloy::hex::encode(args.abi_encode());
-    e2e::deploy(user.url(), &user.pk(), Some(args)).await
+    e2e::deploy(account.url(), &account.pk(), Some(args)).await
 }
 
 // ============================================================================
@@ -28,7 +28,7 @@ async fn deploy(user: &User, owner: Address) -> eyre::Result<Address> {
 // ============================================================================
 
 #[e2e::test]
-async fn constructs(alice: User) -> Result<()> {
+async fn constructs(alice: Account) -> Result<()> {
     let alice_addr = alice.address();
     let contract_addr = deploy(&alice, alice_addr).await?;
     let contract = Ownable::new(contract_addr, &alice.wallet);
@@ -41,7 +41,7 @@ async fn constructs(alice: User) -> Result<()> {
 
 #[e2e::test]
 async fn emits_ownership_transfer_during_construction(
-    alice: User,
+    alice: Account,
 ) -> Result<()> {
     let alice_addr = alice.address();
 
@@ -64,7 +64,7 @@ async fn emits_ownership_transfer_during_construction(
 }
 
 #[e2e::test]
-async fn rejects_zero_address_initial_owner(alice: User) -> Result<()> {
+async fn rejects_zero_address_initial_owner(alice: Account) -> Result<()> {
     let err = deploy(&alice, Address::ZERO)
         .await
         .expect_err("should not deploy due to `OwnableInvalidOwner`");
@@ -81,7 +81,7 @@ async fn rejects_zero_address_initial_owner(alice: User) -> Result<()> {
 }
 
 #[e2e::test]
-async fn transfers_ownership(alice: User, bob: User) -> Result<()> {
+async fn transfers_ownership(alice: Account, bob: Account) -> Result<()> {
     let alice_addr = alice.address();
     let bob_addr = bob.address();
 
@@ -102,8 +102,8 @@ async fn transfers_ownership(alice: User, bob: User) -> Result<()> {
 
 #[e2e::test]
 async fn prevents_non_owners_from_transferring(
-    alice: User,
-    bob: User,
+    alice: Account,
+    bob: Account,
 ) -> Result<()> {
     let alice_addr = alice.address();
     let bob_addr = bob.address();
@@ -121,7 +121,7 @@ async fn prevents_non_owners_from_transferring(
 }
 
 #[e2e::test]
-async fn guards_against_stuck_state(alice: User) -> Result<()> {
+async fn guards_against_stuck_state(alice: Account) -> Result<()> {
     let alice_addr = alice.address();
     let contract_addr = deploy(&alice, alice_addr).await?;
     let contract = Ownable::new(contract_addr, &alice.wallet);
@@ -137,7 +137,7 @@ async fn guards_against_stuck_state(alice: User) -> Result<()> {
 }
 
 #[e2e::test]
-async fn loses_ownership_after_renouncement(alice: User) -> Result<()> {
+async fn loses_ownership_after_renouncement(alice: Account) -> Result<()> {
     let alice_addr = alice.address();
     let contract_addr = deploy(&alice, alice_addr).await?;
     let contract = Ownable::new(contract_addr, &alice.wallet);
@@ -156,8 +156,8 @@ async fn loses_ownership_after_renouncement(alice: User) -> Result<()> {
 
 #[e2e::test]
 async fn prevents_non_owners_from_renouncement(
-    alice: User,
-    bob: User,
+    alice: Account,
+    bob: Account,
 ) -> Result<()> {
     let alice_addr = alice.address();
     let bob_addr = bob.address();
diff --git a/lib/e2e-proc/Cargo.toml b/lib/e2e-proc/Cargo.toml
index 03af513..f149d54 100644
--- a/lib/e2e-proc/Cargo.toml
+++ b/lib/e2e-proc/Cargo.toml
@@ -9,9 +9,9 @@ keywords.workspace = true
 repository.workspace = true
 
 [dependencies]
-proc-macro2 = "1.0.79"
-quote = "1.0.35"
-syn = { version = "2.0.58", features = ["full"] }
+proc-macro2.workspace = true
+quote.workspace = true
+syn.workspace = true
 
 [lib]
 proc-macro = true
diff --git a/lib/e2e-proc/src/lib.rs b/lib/e2e-proc/src/lib.rs
index 739bf86..f0f7bae 100644
--- a/lib/e2e-proc/src/lib.rs
+++ b/lib/e2e-proc/src/lib.rs
@@ -3,15 +3,15 @@ use proc_macro::TokenStream;
 
 mod test;
 
-/// Defines an end-to-end Stylus contract test that sets up `e2e::User`s based
-/// on the function's parameters.
+/// Defines an end-to-end Stylus contract test that sets up `e2e::Account`s
+/// based on the function's parameters.
 ///
 /// # Examples
 ///
 /// ```rust,ignore
 /// #[e2e::test]
-/// async fn foo(alice: User, bob: User) -> eyre::Result<()> {
-///     let charlie = User::new().await?;
+/// async fn foo(alice: Account, bob: Account) -> eyre::Result<()> {
+///     let charlie = Account::new().await?;
 ///     // ...
 /// }
 /// ```
diff --git a/lib/e2e-proc/src/test.rs b/lib/e2e-proc/src/test.rs
index f469067..f981d89 100644
--- a/lib/e2e-proc/src/test.rs
+++ b/lib/e2e-proc/src/test.rs
@@ -13,7 +13,7 @@ macro_rules! error {
     }};
 }
 
-/// Defines an end-to-end test that injects test users through parameters.
+/// Defines an end-to-end test that injects test accounts through parameters.
 ///
 /// For more information see [`crate::test`].
 pub(crate) fn test(_attr: &TokenStream, input: TokenStream) -> TokenStream {
@@ -25,21 +25,21 @@ pub(crate) fn test(_attr: &TokenStream, input: TokenStream) -> TokenStream {
     let fn_stmts = &item_fn.block.stmts;
     let fn_args = &sig.inputs;
 
-    let user_declarations = fn_args.into_iter().map(|arg| {
+    let account_declarations = fn_args.into_iter().map(|arg| {
         let FnArg::Typed(arg) = arg else {
             error!(arg, "unexpected receiver argument in test signature");
         };
-        let user_arg_binding = &arg.pat;
-        let user_ty = &arg.ty;
+        let account_arg_binding = &arg.pat;
+        let account_ty = &arg.ty;
         quote! {
-            let #user_arg_binding = #user_ty::new().await?;
+            let #account_arg_binding = #account_ty::new().await?;
         }
     });
     quote! {
         #( #attrs )*
         #[tokio::test]
         async fn #fn_name() #fn_return_type {
-            #( #user_declarations )*
+            #( #account_declarations )*
             #( #fn_stmts )*
         }
     }
diff --git a/lib/e2e/Cargo.toml b/lib/e2e/Cargo.toml
index 8c5104a..38ddd23 100644
--- a/lib/e2e/Cargo.toml
+++ b/lib/e2e/Cargo.toml
@@ -10,13 +10,14 @@ repository.workspace = true
 
 [dependencies]
 alloy.workspace = true
-tokio = { version = "1.12.0", features = ["full", "process"] }
-eyre = "0.6.8"
-regex = "1.10.4"
-once_cell = "1.19.0"
-koba = "0.1.0"
+tokio = { workspace = true, features = ["process"] }
+eyre.workspace = true
+regex.workspace = true
+once_cell.workspace = true
+koba.workspace = true
 e2e-proc = { path = "../e2e-proc" }
 toml = "0.8.13"
 
 [lints]
-workspace = true
\ No newline at end of file
+workspace = true
+
diff --git a/lib/e2e/README.md b/lib/e2e/README.md
index 241f378..2935e12 100644
--- a/lib/e2e/README.md
+++ b/lib/e2e/README.md
@@ -15,14 +15,14 @@ tests suite using the `e2e` crate.
 
 [GitHub workflow]: ../../.github/workflows/e2e-tests.yml
 
-### Users
+### Accounts
 
 Decorate your tests with the `test` procedural macro: a thin wrapper over
-`tokio::test` that sets up `User`s for your test.
+`tokio::test` that sets up `Account`s for your test.
 
 ```rust,ignore
 #[e2e::test]
-async fn users_are_funded(alice: User) -> eyre::Result<()> {
+async fn accounts_are_funded(alice: Account) -> eyre::Result<()> {
     let balance = alice.wallet.get_balance(alice.address()).await?;
     let expected = parse_ether("10")?;
     assert_eq!(expected, balance);
@@ -30,18 +30,18 @@ async fn users_are_funded(alice: User) -> eyre::Result<()> {
 }
 ```
 
-A `User` is a thin wrapper over a [`PrivateKeySigner`] and an `alloy` provider with a
+A `Account` is a thin wrapper over a [`PrivateKeySigner`] and an `alloy` provider with a
 [`WalletFiller`]. Both of them are connected to the RPC endpoint defined by the
-`RPC_URL` environment variable. This means that a `User` is the main proxy
+`RPC_URL` environment variable. This means that a `Account` is the main proxy
 between the RPC and the test code.
 
-All users start with 10 ETH as balance. You can have multiple users as
-parameters of your test function, or you can create new users separately:
+All accounts start with 10 ETH as balance. You can have multiple accounts as
+parameters of your test function, or you can create new accounts separately:
 
 ```rust,ignore
 #[e2e::test]
-async fn foo(alice: User, bob: User) -> eyre::Result<()> {
-    let charlie = User::new().await?;
+async fn foo(alice: Account, bob: Account) -> eyre::Result<()> {
+    let charlie = Account::new().await?;
     // ...
 }
 ```
@@ -98,7 +98,7 @@ You can then make calls to your contract:
 
 ```rust,ignore
 #[e2e::test]
-async fn constructs(alice: User) -> Result<()> {
+async fn constructs(alice: Account) -> Result<()> {
     let contract_addr = deploy(alice.url(), &alice.pk()).await?;
     let contract = Erc20::new(contract_addr, &alice.wallet);
 
diff --git a/lib/e2e/src/user.rs b/lib/e2e/src/account.rs
similarity index 74%
rename from lib/e2e/src/user.rs
rename to lib/e2e/src/account.rs
index 5677126..e1cdb8c 100644
--- a/lib/e2e/src/user.rs
+++ b/lib/e2e/src/account.rs
@@ -13,26 +13,26 @@ use crate::{
     system::{Wallet, RPC_URL_ENV_VAR_NAME},
 };
 
-/// Type that corresponds to a test user.
+/// Type that corresponds to a test account.
 #[derive(Clone, Debug)]
-pub struct User {
+pub struct Account {
     /// The account's local private key wrapper.
     pub signer: PrivateKeySigner,
     /// The account's wallet -- an `alloy` provider with a `WalletFiller`.
     pub wallet: Wallet,
 }
 
-impl User {
-    /// Create a new user.
+impl Account {
+    /// Create a new account.
     ///
     /// # Errors
     ///
     /// May fail if funding the newly created account fails.
     pub async fn new() -> Result<Self> {
-        UserFactory::create().await
+        AccountFactory::create().await
     }
 
-    /// Get a hex-encoded String representing this user's private key.
+    /// Get a hex-encoded String representing this account's private key.
     #[must_use]
     pub fn pk(&self) -> String {
         alloy::hex::encode(self.signer.to_bytes())
@@ -44,26 +44,27 @@ impl User {
         self.signer.address()
     }
 
-    /// The rpc endpoint this user's provider is connect to.
+    /// The rpc endpoint this account's provider is connect to.
     #[must_use]
     pub fn url(&self) -> &str {
         self.wallet.client().transport().url()
     }
 }
 
-/// A unit struct used as a synchronization mechanism in [`SYNC_USER_FACTORY`].
-struct UserFactory;
+/// A unit struct used as a synchronization mechanism in
+/// [`SYNC_ACCOUNT_FACTORY`].
+struct AccountFactory;
 
-impl UserFactory {
+impl AccountFactory {
     /// Get access to the factory in a synchronized manner.
     async fn lock() -> MutexGuard<'static, Self> {
-        /// Since after wallet generation users get funded in the nitro test
-        /// node from a single "god" wallet, we must synchronize user
+        /// Since after wallet generation accounts get funded in the nitro test
+        /// node from a single "god" wallet, we must synchronize account
         /// creation (otherwise the nonce will be too low).
-        static SYNC_USER_FACTORY: Lazy<Mutex<UserFactory>> =
-            Lazy::new(|| Mutex::new(UserFactory));
+        static SYNC_ACCOUNT_FACTORY: Lazy<Mutex<AccountFactory>> =
+            Lazy::new(|| Mutex::new(AccountFactory));
 
-        SYNC_USER_FACTORY.lock().await
+        SYNC_ACCOUNT_FACTORY.lock().await
     }
 
     /// Create new account and fund it via nitro test node access.
@@ -72,8 +73,8 @@ impl UserFactory {
     ///
     /// May fail if unable to find the path to the node or if funding the newly
     /// created account fails.
-    async fn create() -> eyre::Result<User> {
-        let _lock = UserFactory::lock().await;
+    async fn create() -> eyre::Result<Account> {
+        let _lock = AccountFactory::lock().await;
 
         let signer = PrivateKeySigner::random();
         let addr = signer.address();
@@ -100,10 +101,10 @@ impl UserFactory {
             .on_http(rpc_url);
 
         if output.status.success() {
-            Ok(User { signer, wallet })
+            Ok(Account { signer, wallet })
         } else {
             let err = String::from_utf8_lossy(&output.stderr);
-            bail!("user's wallet wasn't funded - address is {addr}:\n{err}")
+            bail!("account's wallet wasn't funded - address is {addr}:\n{err}")
         }
     }
 }
diff --git a/lib/e2e/src/lib.rs b/lib/e2e/src/lib.rs
index 533e7c8..8b3a33f 100644
--- a/lib/e2e/src/lib.rs
+++ b/lib/e2e/src/lib.rs
@@ -1,18 +1,18 @@
 #![doc = include_str!("../README.md")]
+mod account;
 mod deploy;
 mod environment;
 mod error;
 mod event;
 mod project;
 mod system;
-mod user;
 
+pub use account::Account;
 pub use deploy::deploy;
 pub use e2e_proc::test;
 pub use error::{Panic, PanicCode, Revert};
 pub use event::EventExt;
 pub use system::{provider, Provider, Wallet};
-pub use user::User;
 
 /// This macro provides a shorthand for broadcasting the transaction to the
 /// network.
@@ -23,7 +23,7 @@ pub use user::User;
 ///
 /// ```rust,ignore
 /// #[e2e::test]
-/// async fn foo(alice: User) -> eyre::Result<()> {
+/// async fn foo(alice: Account) -> eyre::Result<()> {
 ///     let contract_addr = deploy(alice.url(), &alice.pk()).await?;
 ///     let contract = Erc721::new(contract_addr, &alice.wallet);
 ///
@@ -48,7 +48,7 @@ macro_rules! send {
 ///
 /// ```rust,ignore
 /// #[e2e::test]
-/// async fn foo(alice: User) -> eyre::Result<()> {
+/// async fn foo(alice: Account) -> eyre::Result<()> {
 ///     let contract_addr = deploy(alice.url(), &alice.pk()).await?;
 ///     let contract = Erc721::new(contract_addr, &alice.wallet);
 ///
@@ -74,7 +74,7 @@ macro_rules! watch {
 ///
 /// ```rust,ignore
 /// #[e2e::test]
-/// async fn foo(alice: User) -> eyre::Result<()> {
+/// async fn foo(alice: Account) -> eyre::Result<()> {
 ///     let contract_addr = deploy(alice.url(), &alice.pk()).await?;
 ///     let contract = Erc721::new(contract_addr, &alice.wallet);
 ///