diff --git a/substrate/frame/support/src/storage/bounded_vec.rs b/substrate/frame/support/src/storage/bounded_vec.rs
index 5b253f76333be4306a0d4de267cf09e269d5ca27..f441ba39b8843cd8b2dde9ce95d119490ef9e3c3 100644
--- a/substrate/frame/support/src/storage/bounded_vec.rs
+++ b/substrate/frame/support/src/storage/bounded_vec.rs
@@ -23,7 +23,7 @@ use sp_std::{convert::TryFrom, marker::PhantomData};
 use codec::{FullCodec, Encode, EncodeLike, Decode};
 use core::{ops::{Index, IndexMut}, slice::SliceIndex};
 use crate::{
-	traits::Get,
+	traits::{Get, MaxEncodedLen},
 	storage::{generator, StorageDecodeLength, StorageValue, StorageMap, StorageDoubleMap},
 };
 
@@ -347,6 +347,21 @@ impl<
 	}
 }
 
+impl<T, S> MaxEncodedLen for BoundedVec<T, S>
+where
+	T: BoundedVecValue + MaxEncodedLen,
+	S: Get<u32>,
+	BoundedVec<T, S>: Encode,
+{
+	fn max_encoded_len() -> usize {
+		// BoundedVec<T, S> encodes like Vec<T> which encodes like [T], which is a compact u32
+		// plus each item in the slice:
+		// https://substrate.dev/rustdocs/v3.0.0/src/parity_scale_codec/codec.rs.html#798-808
+		codec::Compact::<u32>::max_encoded_len()
+			.saturating_add(Self::bound().saturating_mul(T::max_encoded_len()))
+	}
+}
+
 #[cfg(test)]
 pub mod test {
 	use super::*;
diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs
index 7ee2b0a56094b5a42b6fb75040e8ecec9f1b54ef..d15356c1e1b09796146f3ba197fb79a303951c9f 100644
--- a/substrate/frame/support/src/traits.rs
+++ b/substrate/frame/support/src/traits.rs
@@ -80,3 +80,6 @@ pub use dispatch::{EnsureOrigin, OriginTrait, UnfilteredDispatchable};
 
 mod voting;
 pub use voting::{CurrencyToVote, SaturatingCurrencyToVote, U128CurrencyToVote};
+
+mod max_encoded_len;
+pub use max_encoded_len::MaxEncodedLen;
diff --git a/substrate/frame/support/src/traits/max_encoded_len.rs b/substrate/frame/support/src/traits/max_encoded_len.rs
new file mode 100644
index 0000000000000000000000000000000000000000..2cf9007d4d62140f9446781e9ebb84c6adcd5846
--- /dev/null
+++ b/substrate/frame/support/src/traits/max_encoded_len.rs
@@ -0,0 +1,132 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use codec::{Compact, Encode};
+use impl_trait_for_tuples::impl_for_tuples;
+use sp_std::{mem, marker::PhantomData};
+
+/// Items implementing `MaxEncodedLen` have a statically known maximum encoded size.
+///
+/// Some containers, such as `BoundedVec`, have enforced size limits and this trait
+/// can be implemented accurately. Other containers, such as `StorageMap`, do not have enforced size
+/// limits. For those containers, it is necessary to make a documented assumption about the maximum
+/// usage, and compute the max encoded length based on that assumption.
+pub trait MaxEncodedLen: Encode {
+	/// Upper bound, in bytes, of the maximum encoded size of this item.
+	fn max_encoded_len() -> usize;
+}
+
+macro_rules! impl_primitives {
+	( $($t:ty),+ ) => {
+		$(
+			impl MaxEncodedLen for $t {
+				fn max_encoded_len() -> usize {
+					mem::size_of::<$t>()
+				}
+			}
+		)+
+	};
+}
+
+impl_primitives!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, bool);
+
+macro_rules! impl_compact {
+	($( $t:ty => $e:expr; )*) => {
+		$(
+			impl MaxEncodedLen for Compact<$t> {
+				fn max_encoded_len() -> usize {
+					$e
+				}
+			}
+		)*
+	};
+}
+
+impl_compact!(
+	// https://github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L261
+	u8 => 2;
+	// https://github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L291
+	u16 => 4;
+	// https://github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L326
+	u32 => 5;
+	// https://github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L369
+	u64 => 9;
+	// https://github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L413
+	u128 => 17;
+);
+
+// impl_for_tuples for values 19 and higher fails because that's where the WrapperTypeEncode impl stops.
+#[impl_for_tuples(18)]
+impl MaxEncodedLen for Tuple {
+	fn max_encoded_len() -> usize {
+		let mut len: usize = 0;
+		for_tuples!( #( len = len.saturating_add(Tuple::max_encoded_len()); )* );
+		len
+	}
+}
+
+impl<T: MaxEncodedLen, const N: usize> MaxEncodedLen for [T; N] {
+	fn max_encoded_len() -> usize {
+		T::max_encoded_len().saturating_mul(N)
+	}
+}
+
+impl<T: MaxEncodedLen> MaxEncodedLen for Option<T> {
+	fn max_encoded_len() -> usize {
+		T::max_encoded_len().saturating_add(1)
+	}
+}
+
+impl<T, E> MaxEncodedLen for Result<T, E>
+where
+	T: MaxEncodedLen,
+	E: MaxEncodedLen,
+{
+	fn max_encoded_len() -> usize {
+		T::max_encoded_len().max(E::max_encoded_len()).saturating_add(1)
+	}
+}
+
+impl<T> MaxEncodedLen for PhantomData<T> {
+	fn max_encoded_len() -> usize {
+		0
+	}
+}
+
+#[cfg(test)]
+mod tests {
+	use super::*;
+
+	macro_rules! test_compact_length {
+		($(fn $name:ident($t:ty);)*) => {
+			$(
+				#[test]
+				fn $name() {
+					assert_eq!(Compact(<$t>::MAX).encode().len(), Compact::<$t>::max_encoded_len());
+				}
+			)*
+		};
+	}
+
+	test_compact_length!(
+		fn compact_u8(u8);
+		fn compact_u16(u16);
+		fn compact_u32(u32);
+		fn compact_u64(u64);
+		fn compact_u128(u128);
+	);
+}