diff --git a/substrate/primitives/consensus/sassafras/src/vrf.rs b/substrate/primitives/consensus/sassafras/src/vrf.rs
index bdbac0aae03774d589f52054906f56ee6c7056a9..afcb1d09f8f6832f5b794543f1f2896366859a4e 100644
--- a/substrate/primitives/consensus/sassafras/src/vrf.rs
+++ b/substrate/primitives/consensus/sassafras/src/vrf.rs
@@ -23,10 +23,16 @@ use sp_consensus_slots::Slot;
 use sp_std::vec::Vec;
 
 pub use sp_core::bandersnatch::{
-	ring_vrf::{RingContext, RingProver, RingVerifier, RingVerifierData, RingVrfSignature},
+	ring_vrf::{RingProver, RingVerifier, RingVerifierData, RingVrfSignature},
 	vrf::{VrfInput, VrfOutput, VrfSignData, VrfSignature},
 };
 
+/// Ring VRF domain size for Sassafras consensus.
+pub const RING_VRF_DOMAIN_SIZE: u32 = 2048;
+
+/// Bandersnatch VRF [`RingContext`] specialization for Sassafras using [`RING_VRF_DOMAIN_SIZE`].
+pub type RingContext = sp_core::bandersnatch::ring_vrf::RingContext<RING_VRF_DOMAIN_SIZE>;
+
 fn vrf_input_from_data(
 	domain: &[u8],
 	data: impl IntoIterator<Item = impl AsRef<[u8]>>,
diff --git a/substrate/primitives/core/src/bandersnatch.rs b/substrate/primitives/core/src/bandersnatch.rs
index 1d666f13b6275dea2cc88ba8216844a703e26045..b91c001a7de03952dc87b16984d315852958a825 100644
--- a/substrate/primitives/core/src/bandersnatch.rs
+++ b/substrate/primitives/core/src/bandersnatch.rs
@@ -39,7 +39,7 @@ use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
 use scale_info::TypeInfo;
 
 use sp_runtime_interface::pass_by::PassByInner;
-use sp_std::{boxed::Box, vec::Vec};
+use sp_std::{vec, vec::Vec};
 
 /// Identifier used to match public keys against bandersnatch-vrf keys.
 pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"band");
@@ -622,31 +622,25 @@ pub mod ring_vrf {
 	pub use bandersnatch_vrfs::ring::{RingProof, RingProver, RingVerifier, KZG};
 	use bandersnatch_vrfs::{ring::VerifierKey, CanonicalDeserialize, PublicKey};
 
-	/// Ring max size (keyset max size).
-	pub const RING_MAX_SIZE: u32 = RING_DOMAIN_MAX_SIZE - RING_DOMAIN_OVERHEAD;
+	/// Overhead in the domain size with respect to the supported ring size.
+	///
+	/// Some bits of the domain are reserved for the zk-proof to work.
+	pub const RING_DOMAIN_OVERHEAD: u32 = 257;
 
-	/// Ring domain max size.
-	pub const RING_DOMAIN_MAX_SIZE: u32 = 2048;
+	// Max size of serialized ring-vrf context given `domain_len`.
+	pub(crate) const fn ring_context_serialized_size(domain_len: u32) -> usize {
+		// const G1_POINT_COMPRESSED_SIZE: usize = 48;
+		// const G2_POINT_COMPRESSED_SIZE: usize = 96;
+		const G1_POINT_UNCOMPRESSED_SIZE: usize = 96;
+		const G2_POINT_UNCOMPRESSED_SIZE: usize = 192;
+		const OVERHEAD_SIZE: usize = 20;
+		const G2_POINTS_NUM: usize = 2;
+		let g1_points_num = 3 * domain_len as usize + 1;
 
-	/// Overhead in the domain size over the max ring size.
-	///
-	/// Some bits of the domain are reserved for the zk proof to work.
-	pub(crate) const RING_DOMAIN_OVERHEAD: u32 = 257;
-
-	// Max size of serialized ring-vrf context params.
-	//
-	// The actual size is dependent on the ring domain size and this value
-	// has been computed for `RING_DOMAIN_MAX_SIZE` with compression disabled
-	// for performance reasons.
-	//
-	// 1024 uncompressed
-	// pub(crate) const RING_CONTEXT_SERIALIZED_MAX_SIZE: usize = 295412;
-	// 1024 compressed
-	// pub(crate) const RING_CONTEXT_SERIALIZED_MAX_SIZE: usize = 147716;
-	// 2048 uncompressed
-	pub(crate) const RING_CONTEXT_SERIALIZED_MAX_SIZE: usize = 590324;
-	// 2048 compressed
-	// pub(crate) const RING_CONTEXT_SERIALIZED_MAX_SIZE: usize = 295172;
+		OVERHEAD_SIZE +
+			g1_points_num * G1_POINT_UNCOMPRESSED_SIZE +
+			G2_POINTS_NUM * G2_POINT_UNCOMPRESSED_SIZE
+	}
 
 	pub(crate) const RING_VERIFIER_DATA_SERIALIZED_SIZE: usize = 388;
 	pub(crate) const RING_SIGNATURE_SERIALIZED_SIZE: usize = 755;
@@ -705,15 +699,17 @@ pub mod ring_vrf {
 	}
 
 	/// Context used to construct ring prover and verifier.
+	///
+	/// Generic parameter `D` represents the ring domain size and drives
+	/// the max number of supported ring members [`RingContext::max_keyset_size`]
+	/// which is equal to `D - RING_DOMAIN_OVERHEAD`.
 	#[derive(Clone)]
-	pub struct RingContext(KZG);
+	pub struct RingContext<const D: u32>(KZG);
 
-	impl RingContext {
+	impl<const D: u32> RingContext<D> {
 		/// Build an dummy instance for testing purposes.
-		///
-		/// `domain_size` is set to `RING_DOMAIN_MAX_SIZE`.
 		pub fn new_testing() -> Self {
-			Self(KZG::testing_kzg_setup([0; 32], RING_DOMAIN_MAX_SIZE))
+			Self(KZG::testing_kzg_setup([0; 32], D))
 		}
 
 		/// Get the keyset max size.
@@ -761,38 +757,45 @@ pub mod ring_vrf {
 		}
 	}
 
-	impl Encode for RingContext {
+	impl<const D: u32> Encode for RingContext<D> {
 		fn encode(&self) -> Vec<u8> {
-			let mut buf = Box::new([0; RING_CONTEXT_SERIALIZED_MAX_SIZE]);
+			let mut buf = vec![0; ring_context_serialized_size(D)];
 			self.0
 				.serialize_uncompressed(buf.as_mut_slice())
 				.expect("serialization length is constant and checked by test; qed");
-			buf.encode()
+			buf
 		}
 	}
 
-	impl Decode for RingContext {
-		fn decode<R: codec::Input>(i: &mut R) -> Result<Self, codec::Error> {
-			let buf = <Box<[u8; RING_CONTEXT_SERIALIZED_MAX_SIZE]>>::decode(i)?;
+	impl<const D: u32> Decode for RingContext<D> {
+		fn decode<R: codec::Input>(input: &mut R) -> Result<Self, codec::Error> {
+			let mut buf = vec![0; ring_context_serialized_size(D)];
+			input.read(&mut buf[..])?;
 			let kzg = KZG::deserialize_uncompressed_unchecked(buf.as_slice())
 				.map_err(|_| "KZG decode error")?;
 			Ok(RingContext(kzg))
 		}
 	}
 
-	impl EncodeLike for RingContext {}
+	impl<const D: u32> EncodeLike for RingContext<D> {}
 
-	impl MaxEncodedLen for RingContext {
+	impl<const D: u32> MaxEncodedLen for RingContext<D> {
 		fn max_encoded_len() -> usize {
-			<[u8; RING_CONTEXT_SERIALIZED_MAX_SIZE]>::max_encoded_len()
+			ring_context_serialized_size(D)
 		}
 	}
 
-	impl TypeInfo for RingContext {
-		type Identity = [u8; RING_CONTEXT_SERIALIZED_MAX_SIZE];
+	impl<const D: u32> TypeInfo for RingContext<D> {
+		type Identity = Self;
 
 		fn type_info() -> scale_info::Type {
-			Self::Identity::type_info()
+			let path = scale_info::Path::new("RingContext", module_path!());
+			let array_type_def = scale_info::TypeDefArray {
+				len: ring_context_serialized_size(D) as u32,
+				type_param: scale_info::MetaType::new::<u8>(),
+			};
+			let type_def = scale_info::TypeDef::Array(array_type_def);
+			scale_info::Type { path, type_params: Vec::new(), type_def, docs: Vec::new() }
 		}
 	}
 
@@ -903,7 +906,11 @@ pub mod ring_vrf {
 mod tests {
 	use super::{ring_vrf::*, vrf::*, *};
 	use crate::crypto::{VrfPublic, VrfSecret, DEV_PHRASE};
+
 	const DEV_SEED: &[u8; SEED_SERIALIZED_SIZE] = &[0xcb; SEED_SERIALIZED_SIZE];
+	const TEST_DOMAIN_SIZE: u32 = 1024;
+
+	type TestRingContext = RingContext<TEST_DOMAIN_SIZE>;
 
 	#[allow(unused)]
 	fn b2h(bytes: &[u8]) -> String {
@@ -916,10 +923,10 @@ mod tests {
 
 	#[test]
 	fn backend_assumptions_sanity_check() {
-		let kzg = KZG::testing_kzg_setup([0; 32], RING_DOMAIN_MAX_SIZE);
-		assert_eq!(kzg.max_keyset_size() as u32, RING_MAX_SIZE);
+		let kzg = KZG::testing_kzg_setup([0; 32], TEST_DOMAIN_SIZE);
+		assert_eq!(kzg.max_keyset_size() as u32, TEST_DOMAIN_SIZE - RING_DOMAIN_OVERHEAD);
 
-		assert_eq!(kzg.uncompressed_size(), RING_CONTEXT_SERIALIZED_MAX_SIZE);
+		assert_eq!(kzg.uncompressed_size(), ring_context_serialized_size(TEST_DOMAIN_SIZE));
 
 		let pks: Vec<_> = (0..16)
 			.map(|i| SecretKey::from_seed(&[i as u8; 32]).to_public().0.into())
@@ -1071,7 +1078,7 @@ mod tests {
 
 	#[test]
 	fn ring_vrf_sign_verify() {
-		let ring_ctx = RingContext::new_testing();
+		let ring_ctx = TestRingContext::new_testing();
 
 		let mut pks: Vec<_> = (0..16).map(|i| Pair::from_seed(&[i as u8; 32]).public()).collect();
 		assert!(pks.len() <= ring_ctx.max_keyset_size());
@@ -1097,7 +1104,7 @@ mod tests {
 
 	#[test]
 	fn ring_vrf_sign_verify_with_out_of_ring_key() {
-		let ring_ctx = RingContext::new_testing();
+		let ring_ctx = TestRingContext::new_testing();
 
 		let pks: Vec<_> = (0..16).map(|i| Pair::from_seed(&[i as u8; 32]).public()).collect();
 		let pair = Pair::from_seed(DEV_SEED);
@@ -1116,7 +1123,7 @@ mod tests {
 
 	#[test]
 	fn ring_vrf_make_bytes_matches() {
-		let ring_ctx = RingContext::new_testing();
+		let ring_ctx = TestRingContext::new_testing();
 
 		let mut pks: Vec<_> = (0..16).map(|i| Pair::from_seed(&[i as u8; 32]).public()).collect();
 		assert!(pks.len() <= ring_ctx.max_keyset_size());
@@ -1145,7 +1152,7 @@ mod tests {
 
 	#[test]
 	fn encode_decode_ring_vrf_signature() {
-		let ring_ctx = RingContext::new_testing();
+		let ring_ctx = TestRingContext::new_testing();
 
 		let mut pks: Vec<_> = (0..16).map(|i| Pair::from_seed(&[i as u8; 32]).public()).collect();
 		assert!(pks.len() <= ring_ctx.max_keyset_size());
@@ -1177,13 +1184,15 @@ mod tests {
 
 	#[test]
 	fn encode_decode_ring_vrf_context() {
-		let ctx1 = RingContext::new_testing();
+		let ctx1 = TestRingContext::new_testing();
 		let enc1 = ctx1.encode();
 
-		assert_eq!(enc1.len(), RING_CONTEXT_SERIALIZED_MAX_SIZE);
-		assert_eq!(RingContext::max_encoded_len(), RING_CONTEXT_SERIALIZED_MAX_SIZE);
+		let _ti = <TestRingContext as TypeInfo>::type_info();
+
+		assert_eq!(enc1.len(), ring_context_serialized_size(TEST_DOMAIN_SIZE));
+		assert_eq!(enc1.len(), TestRingContext::max_encoded_len());
 
-		let ctx2 = RingContext::decode(&mut enc1.as_slice()).unwrap();
+		let ctx2 = TestRingContext::decode(&mut enc1.as_slice()).unwrap();
 		let enc2 = ctx2.encode();
 
 		assert_eq!(enc1, enc2);
@@ -1191,7 +1200,7 @@ mod tests {
 
 	#[test]
 	fn encode_decode_verifier_data() {
-		let ring_ctx = RingContext::new_testing();
+		let ring_ctx = TestRingContext::new_testing();
 
 		let pks: Vec<_> = (0..16).map(|i| Pair::from_seed(&[i as u8; 32]).public()).collect();
 		assert!(pks.len() <= ring_ctx.max_keyset_size());
diff --git a/substrate/primitives/keystore/src/testing.rs b/substrate/primitives/keystore/src/testing.rs
index 08110e8e497919db42d52d3d6e75194e0d4e3130..9b59b6d09ea08cdae24eb90ee06d8d2b34aef20c 100644
--- a/substrate/primitives/keystore/src/testing.rs
+++ b/substrate/primitives/keystore/src/testing.rs
@@ -525,7 +525,7 @@ mod tests {
 
 		let store = MemoryKeystore::new();
 
-		let ring_ctx = bandersnatch::ring_vrf::RingContext::new_testing();
+		let ring_ctx = bandersnatch::ring_vrf::RingContext::<1024>::new_testing();
 
 		let mut pks: Vec<_> = (0..16)
 			.map(|i| bandersnatch::Pair::from_seed(&[i as u8; 32]).public())