Why is CheckNonZeroSender a signed extension rather than part of the UncheckedExtrinsic::check() when checking the payload for a bad proof? What exactly does the non-zero check do? It appears to be checking all the bytes to see if they are zero?

if who.using_encoded(|d| d.iter().all(|x| *x == 0)) {
    return Err(TransactionValidityError::Invalid(InvalidTransaction::BadSigner))

2 Answers 2


I think this because the all-zero address is a 'well-known' key.

It is dangerous, and people love to use the default value. Maybe get an account from Vec::default() in some corner cases.

So we forbid using this address to send transactions.

And you might want to learn more detail about the all-zero address.

Why does the all 0 public key have a known private key in SR25519 and ED25519?

  • It's only "dangerous" because BTC and ETH people adopted a flawed convention. hash-to-curve was always the sensible solution because it lets you encode a non-spendable messages, like the reason why you burned tokens. If I just burn to zero then maybe I can benefit from the burn in multiple ways in unrelated applications, which then makes the burn insecure. Commented Nov 30, 2022 at 13:37

sr25519 and ed25519 the private key for the all-zeros-public-key is known. However, in ecdsa this isn't the case and people used the all-zeros-public-key as destination when they wanted to "burn" tokens, aka make them inaccessible. To mimic the same behavior, there exists this CheckNonZeroSender extension in Substrate.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.