diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock
index 2db192dcbf1a165ab19b2174647467cdaf7f4cef..b5506b12594362e4e3f299bd62faf7d18c433ba2 100644
--- a/substrate/Cargo.lock
+++ b/substrate/Cargo.lock
@@ -1788,6 +1788,20 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "ed25519-zebra"
+version = "3.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "403ef3e961ab98f0ba902771d29f842058578bb1ce7e3c59dad5a6a93e784c69"
+dependencies = [
+ "curve25519-dalek 3.0.2",
+ "hex",
+ "rand_core 0.6.2",
+ "sha2 0.9.8",
+ "thiserror",
+ "zeroize",
+]
+
 [[package]]
 name = "either"
 version = "1.6.1"
@@ -9624,7 +9638,7 @@ dependencies = [
  "byteorder",
  "criterion",
  "dyn-clonable",
- "ed25519-dalek",
+ "ed25519-zebra",
  "futures",
  "hash-db",
  "hash256-std-hasher",
diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml
index 1f22343c002a86ff577e34dee3aec31fe6adb63f..e2907716ca9f2ac92e9a75e40aec1df09c310d95 100644
--- a/substrate/Cargo.toml
+++ b/substrate/Cargo.toml
@@ -258,7 +258,7 @@ crc32fast = { opt-level = 3 }
 crossbeam-deque = { opt-level = 3 }
 crypto-mac = { opt-level = 3 }
 curve25519-dalek = { opt-level = 3 }
-ed25519-dalek = { opt-level = 3 }
+ed25519-zebra = { opt-level = 3 }
 flate2 = { opt-level = 3 }
 futures-channel = { opt-level = 3 }
 hashbrown = { opt-level = 3 }
diff --git a/substrate/primitives/core/Cargo.toml b/substrate/primitives/core/Cargo.toml
index fced67829314088cb9dcafd4b6ae9b4ebc4e1bc7..2cdfd04e942d7e718718818da6d394408257e906 100644
--- a/substrate/primitives/core/Cargo.toml
+++ b/substrate/primitives/core/Cargo.toml
@@ -47,7 +47,7 @@ thiserror = { version = "1.0.30", optional = true }
 bitflags = "1.3"
 
 # full crypto
-ed25519-dalek = { version = "1.0.1", default-features = false, features = ["u64_backend", "alloc"], optional = true }
+ed25519-zebra = { version = "3.0.0", default-features = false, optional = true}
 blake2-rfc = { version = "0.2.18", default-features = false, optional = true }
 schnorrkel = { version = "0.9.1", features = [
 	"preaudit_deprecated",
@@ -97,7 +97,7 @@ std = [
 	"sp-std/std",
 	"serde",
 	"blake2-rfc/std",
-	"ed25519-dalek/std",
+	"ed25519-zebra",
 	"hex/std",
 	"base58",
 	"substrate-bip39",
@@ -127,7 +127,7 @@ std = [
 # or Intel SGX.
 # For the regular wasm runtime builds this should not be used.
 full_crypto = [
-	"ed25519-dalek",
+	"ed25519-zebra",
 	"blake2-rfc",
 	"schnorrkel",
 	"hex",
diff --git a/substrate/primitives/core/src/ed25519.rs b/substrate/primitives/core/src/ed25519.rs
index 177af0651c0efe678574e30b0eeabf5b9267998a..0553cf4843df5511968a85b71044db355d3750b5 100644
--- a/substrate/primitives/core/src/ed25519.rs
+++ b/substrate/primitives/core/src/ed25519.rs
@@ -39,7 +39,9 @@ use crate::crypto::{DeriveJunction, Pair as TraitPair, SecretStringError};
 #[cfg(feature = "std")]
 use bip39::{Language, Mnemonic, MnemonicType};
 #[cfg(feature = "full_crypto")]
-use ed25519_dalek::{Signer as _, Verifier as _};
+use core::convert::TryFrom;
+#[cfg(feature = "full_crypto")]
+use ed25519_zebra::{SigningKey, VerificationKey};
 #[cfg(feature = "std")]
 use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
 use sp_runtime_interface::pass_by::PassByInner;
@@ -75,17 +77,10 @@ pub struct Public(pub [u8; 32]);
 
 /// A key pair.
 #[cfg(feature = "full_crypto")]
-pub struct Pair(ed25519_dalek::Keypair);
-
-#[cfg(feature = "full_crypto")]
-impl Clone for Pair {
-	fn clone(&self) -> Self {
-		Pair(ed25519_dalek::Keypair {
-			public: self.0.public,
-			secret: ed25519_dalek::SecretKey::from_bytes(self.0.secret.as_bytes())
-				.expect("key is always the correct size; qed"),
-		})
-	}
+#[derive(Copy, Clone)]
+pub struct Pair {
+	public: VerificationKey,
+	secret: SigningKey,
 }
 
 impl AsRef<[u8; 32]> for Public {
@@ -456,10 +451,10 @@ impl TraitPair for Pair {
 	///
 	/// You should never need to use this; generate(), generate_with_phrase
 	fn from_seed_slice(seed_slice: &[u8]) -> Result<Pair, SecretStringError> {
-		let secret = ed25519_dalek::SecretKey::from_bytes(seed_slice)
-			.map_err(|_| SecretStringError::InvalidSeedLength)?;
-		let public = ed25519_dalek::PublicKey::from(&secret);
-		Ok(Pair(ed25519_dalek::Keypair { secret, public }))
+		let secret =
+			SigningKey::try_from(seed_slice).map_err(|_| SecretStringError::InvalidSeedLength)?;
+		let public = VerificationKey::from(&secret);
+		Ok(Pair { secret, public })
 	}
 
 	/// Derive a child key from a series of given junctions.
@@ -468,7 +463,7 @@ impl TraitPair for Pair {
 		path: Iter,
 		_seed: Option<Seed>,
 	) -> Result<(Pair, Option<Seed>), DeriveError> {
-		let mut acc = self.0.secret.to_bytes();
+		let mut acc = self.secret.into();
 		for j in path {
 			match j {
 				DeriveJunction::Soft(_cc) => return Err(DeriveError::SoftKeyInPath),
@@ -480,16 +475,12 @@ impl TraitPair for Pair {
 
 	/// Get the public key.
 	fn public(&self) -> Public {
-		let mut r = [0u8; 32];
-		let pk = self.0.public.as_bytes();
-		r.copy_from_slice(pk);
-		Public(r)
+		Public(self.public.into())
 	}
 
 	/// Sign a message.
 	fn sign(&self, message: &[u8]) -> Signature {
-		let r = self.0.sign(message).to_bytes();
-		Signature::from_raw(r)
+		Signature::from_raw(self.secret.sign(message).into())
 	}
 
 	/// Verify a signature on a message. Returns true if the signature is good.
@@ -502,17 +493,17 @@ impl TraitPair for Pair {
 	/// This doesn't use the type system to ensure that `sig` and `pubkey` are the correct
 	/// size. Use it only if you're coming from byte buffers and need the speed.
 	fn verify_weak<P: AsRef<[u8]>, M: AsRef<[u8]>>(sig: &[u8], message: M, pubkey: P) -> bool {
-		let public_key = match ed25519_dalek::PublicKey::from_bytes(pubkey.as_ref()) {
+		let public_key = match VerificationKey::try_from(pubkey.as_ref()) {
 			Ok(pk) => pk,
 			Err(_) => return false,
 		};
 
-		let sig = match ed25519_dalek::Signature::try_from(sig) {
+		let sig = match ed25519_zebra::Signature::try_from(sig) {
 			Ok(s) => s,
 			Err(_) => return false,
 		};
 
-		public_key.verify(message.as_ref(), &sig).is_ok()
+		public_key.verify(&sig, message.as_ref()).is_ok()
 	}
 
 	/// Return a vec filled with raw data.
@@ -524,8 +515,8 @@ impl TraitPair for Pair {
 #[cfg(feature = "full_crypto")]
 impl Pair {
 	/// Get the seed for this key.
-	pub fn seed(&self) -> &Seed {
-		self.0.secret.as_bytes()
+	pub fn seed(&self) -> Seed {
+		self.secret.into()
 	}
 
 	/// Exactly as `from_string` except that if no matches are found then, the the first 32
@@ -577,12 +568,12 @@ mod test {
 	fn seed_and_derive_should_work() {
 		let seed = hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
 		let pair = Pair::from_seed(&seed);
-		assert_eq!(pair.seed(), &seed);
+		assert_eq!(pair.seed(), seed);
 		let path = vec![DeriveJunction::Hard([0u8; 32])];
 		let derived = pair.derive(path.into_iter(), None).ok().unwrap().0;
 		assert_eq!(
 			derived.seed(),
-			&hex!("ede3354e133f9c8e337ddd6ee5415ed4b4ffe5fc7d21e933f4930a3730e5b21c")
+			hex!("ede3354e133f9c8e337ddd6ee5415ed4b4ffe5fc7d21e933f4930a3730e5b21c")
 		);
 	}
 
diff --git a/substrate/primitives/io/src/lib.rs b/substrate/primitives/io/src/lib.rs
index 9bf9345e594c312928356dc25e5d0305fb22987d..7942bafcc2a1bb860c36ea5bfb6c902659ba194c 100644
--- a/substrate/primitives/io/src/lib.rs
+++ b/substrate/primitives/io/src/lib.rs
@@ -1895,6 +1895,7 @@ mod tests {
 		ext.register_extension(TaskExecutorExt::new(TaskExecutor::new()));
 		ext.execute_with(|| {
 			let pair = sr25519::Pair::generate_with_phrase(None).0;
+			let pair_unused = sr25519::Pair::generate_with_phrase(None).0;
 			crypto::start_batch_verify();
 			for it in 0..70 {
 				let msg = format!("Schnorrkel {}!", it);
@@ -1902,8 +1903,10 @@ mod tests {
 				crypto::sr25519_batch_verify(&signature, msg.as_bytes(), &pair.public());
 			}
 
-			// push invlaid
-			crypto::sr25519_batch_verify(&zero_sr_sig(), &Vec::new(), &zero_sr_pub());
+			// push invalid
+			let msg = b"asdf!";
+			let signature = pair.sign(msg);
+			crypto::sr25519_batch_verify(&signature, msg, &pair_unused.public());
 			assert!(!crypto::finish_batch_verify());
 
 			crypto::start_batch_verify();
@@ -1938,10 +1941,10 @@ mod tests {
 		ext.register_extension(TaskExecutorExt::new(TaskExecutor::new()));
 
 		ext.execute_with(|| {
-			// invalid ed25519 signature
+			// valid ed25519 signature
 			crypto::start_batch_verify();
 			crypto::ed25519_batch_verify(&zero_ed_sig(), &Vec::new(), &zero_ed_pub());
-			assert!(!crypto::finish_batch_verify());
+			assert!(crypto::finish_batch_verify());
 
 			// 2 valid ed25519 signatures
 			crypto::start_batch_verify();
@@ -1961,12 +1964,14 @@ mod tests {
 			// 1 valid, 1 invalid ed25519 signature
 			crypto::start_batch_verify();
 
-			let pair = ed25519::Pair::generate_with_phrase(None).0;
+			let pair1 = ed25519::Pair::generate_with_phrase(None).0;
+			let pair2 = ed25519::Pair::generate_with_phrase(None).0;
 			let msg = b"Important message";
-			let signature = pair.sign(msg);
-			crypto::ed25519_batch_verify(&signature, msg, &pair.public());
+			let signature = pair1.sign(msg);
 
 			crypto::ed25519_batch_verify(&zero_ed_sig(), &Vec::new(), &zero_ed_pub());
+			crypto::ed25519_batch_verify(&signature, msg, &pair1.public());
+			crypto::ed25519_batch_verify(&signature, msg, &pair2.public());
 
 			assert!(!crypto::finish_batch_verify());
 
@@ -1993,11 +1998,13 @@ mod tests {
 			// 1 valid sr25519, 1 invalid sr25519
 			crypto::start_batch_verify();
 
-			let pair = sr25519::Pair::generate_with_phrase(None).0;
+			let pair1 = sr25519::Pair::generate_with_phrase(None).0;
+			let pair2 = sr25519::Pair::generate_with_phrase(None).0;
 			let msg = b"Schnorrkcel!";
-			let signature = pair.sign(msg);
-			crypto::sr25519_batch_verify(&signature, msg, &pair.public());
+			let signature = pair1.sign(msg);
 
+			crypto::sr25519_batch_verify(&signature, msg, &pair1.public());
+			crypto::sr25519_batch_verify(&signature, msg, &pair2.public());
 			crypto::sr25519_batch_verify(&zero_sr_sig(), &Vec::new(), &zero_sr_pub());
 
 			assert!(!crypto::finish_batch_verify());