diff --git a/addresses/contrib/test_vars.sh b/addresses/contrib/test_vars.sh
index 88e2d26e1..92dc09406 100644
--- a/addresses/contrib/test_vars.sh
+++ b/addresses/contrib/test_vars.sh
@@ -8,7 +8,7 @@
FEATURES_WITH_STD=""
# Test all these features without "std" enabled.
-FEATURES_WITHOUT_STD=""
+FEATURES_WITHOUT_STD="alloc"
# Run these examples.
EXAMPLES=""
diff --git a/addresses/src/lib.rs b/addresses/src/lib.rs
index b660dad39..32b7bda62 100644
--- a/addresses/src/lib.rs
+++ b/addresses/src/lib.rs
@@ -5,8 +5,12 @@
//! Bitcoin addresses do not appear on chain; rather, they are conventions used by Bitcoin (wallet)
//! software to communicate where coins should be sent and are based on the output type e.g., P2WPKH.
//!
+//! This crate can be used in a no-std environment but requires an allocator.
+//!
//! ref:
+// NB: This crate is empty if `alloc` is not enabled.
+#![cfg(feature = "alloc")]
#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
// Experimental features we need.
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
diff --git a/base58/Cargo.toml b/base58/Cargo.toml
index c6a57da89..9141a1711 100644
--- a/base58/Cargo.toml
+++ b/base58/Cargo.toml
@@ -14,11 +14,12 @@ exclude = ["tests", "contrib"]
[features]
default = ["std"]
-std = ["hashes/std", "internals/std"]
+std = ["alloc", "hashes/std", "internals/std"]
+alloc = ["hashes/alloc", "internals/alloc"]
[dependencies]
-hashes = { package = "bitcoin_hashes", version = "0.14.0", default-features = false, features = ["alloc"] }
-internals = { package = "bitcoin-internals", version = "0.3.0", features = ["alloc"] }
+hashes = { package = "bitcoin_hashes", version = "0.14.0", default-features = false }
+internals = { package = "bitcoin-internals", version = "0.3.0" }
[dev-dependencies]
hex = { package = "hex-conservative", version = "0.2.0", default-features = false, features = ["alloc"] }
diff --git a/base58/contrib/test_vars.sh b/base58/contrib/test_vars.sh
index 88e2d26e1..92dc09406 100644
--- a/base58/contrib/test_vars.sh
+++ b/base58/contrib/test_vars.sh
@@ -8,7 +8,7 @@
FEATURES_WITH_STD=""
# Test all these features without "std" enabled.
-FEATURES_WITHOUT_STD=""
+FEATURES_WITHOUT_STD="alloc"
# Run these examples.
EXAMPLES=""
diff --git a/base58/src/lib.rs b/base58/src/lib.rs
index c449f8c28..38256b352 100644
--- a/base58/src/lib.rs
+++ b/base58/src/lib.rs
@@ -4,6 +4,8 @@
//!
//! This crate can be used in a no-std environment but requires an allocator.
+// NB: This crate is empty if `alloc` is not enabled.
+#![cfg(feature = "alloc")]
#![no_std]
// Experimental features we need.
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml
index 99d413b00..bd7b420f5 100644
--- a/bitcoin/Cargo.toml
+++ b/bitcoin/Cargo.toml
@@ -25,13 +25,13 @@ secp-recovery = ["secp256k1/recovery"]
bitcoinconsensus-std = ["bitcoinconsensus/std", "std"]
[dependencies]
-base58 = { package = "base58ck", version = "0.1.0", default-features = false }
+base58 = { package = "base58ck", version = "0.1.0", default-features = false, features = ["alloc"] }
bech32 = { version = "0.11.0", default-features = false, features = ["alloc"] }
hashes = { package = "bitcoin_hashes", version = "0.14.0", default-features = false, features = ["alloc", "io"] }
hex = { package = "hex-conservative", version = "0.2.0", default-features = false, features = ["alloc"] }
internals = { package = "bitcoin-internals", version = "0.3.0", features = ["alloc"] }
io = { package = "bitcoin-io", version = "0.1.1", default-features = false, features = ["alloc"] }
-primitives = { package = "bitcoin-primitives", version = "0.100.0", default-features = false }
+primitives = { package = "bitcoin-primitives", version = "0.100.0", default-features = false, features = ["alloc"] }
secp256k1 = { version = "0.29.0", default-features = false, features = ["hashes", "alloc"] }
units = { package = "bitcoin-units", version = "0.1.0", default-features = false, features = ["alloc"] }
diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml
index ef0b28b85..db50081fd 100644
--- a/primitives/Cargo.toml
+++ b/primitives/Cargo.toml
@@ -16,14 +16,15 @@ exclude = ["tests", "contrib"]
[features]
default = ["std"]
-std = ["internals/std"]
+std = ["alloc", "internals/std"]
+alloc = ["internals/alloc"]
serde = ["actual-serde", "internals/serde"]
[dependencies]
-internals = { package = "bitcoin-internals", version = "0.3.0", features = ["alloc"] }
+internals = { package = "bitcoin-internals", version = "0.3.0" }
# Do NOT use this as a feature! Use the `serde` feature instead.
-actual-serde = { package = "serde", version = "1.0.103", default-features = false, features = [ "alloc" ], optional = true }
+actual-serde = { package = "serde", version = "1.0.103", default-features = false, optional = true }
[dev-dependencies]
diff --git a/primitives/contrib/test_vars.sh b/primitives/contrib/test_vars.sh
index 03d9a45b3..8bb284700 100644
--- a/primitives/contrib/test_vars.sh
+++ b/primitives/contrib/test_vars.sh
@@ -5,10 +5,10 @@
# shellcheck disable=SC2034
# Test these features with "std" enabled.
-FEATURES_WITH_STD=""
+FEATURES_WITH_STD="serde"
# Test these features without "std" enabled.
-FEATURES_WITHOUT_STD=""
+FEATURES_WITHOUT_STD="alloc serde"
# Run these examples.
EXAMPLES=""
diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs
index d899d4d74..63758ea1f 100644
--- a/primitives/src/lib.rs
+++ b/primitives/src/lib.rs
@@ -4,8 +4,12 @@
//!
//! Primitive data types that are used throughout the [`rust-bitcoin`] ecosystem.
//!
+//! This crate can be used in a no-std environment but requires an allocator.
+//!
//! [`rust-bitcoin`]:
+// NB: This crate is empty if `alloc` is not enabled.
+#![cfg(feature = "alloc")]
#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
// Experimental features we need.
#![cfg_attr(docsrs, feature(doc_auto_cfg))]