diff --git a/Cargo.lock b/Cargo.lock index 67b0ad4def24..62e53dfb8de3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19627,6 +19627,7 @@ dependencies = [ "hash256-std-hasher", "impl-trait-for-tuples", "log", + "num-traits", "parity-scale-codec", "paste", "rand 0.8.5", diff --git a/prdoc/pr_4034.prdoc b/prdoc/pr_4034.prdoc new file mode 100644 index 000000000000..994811b5d672 --- /dev/null +++ b/prdoc/pr_4034.prdoc @@ -0,0 +1,14 @@ +title: "Introduces `TypeWithDefault>`" + +doc: + - audience: Runtime Dev + description: | + This PR introduces a new type `TypeWithDefault>` to be able to provide a + custom default for any type. This can, then, be used to provide the nonce type that returns + the current block number as the default, to avoid replay of immortal transactions. + +crates: +- name: sp-runtime + bump: minor +- name: frame-system + bump: patch diff --git a/substrate/frame/system/src/extensions/check_nonce.rs b/substrate/frame/system/src/extensions/check_nonce.rs index 7504a814aef1..894ab72eb593 100644 --- a/substrate/frame/system/src/extensions/check_nonce.rs +++ b/substrate/frame/system/src/extensions/check_nonce.rs @@ -142,7 +142,7 @@ mod tests { crate::Account::::insert( 1, crate::AccountInfo { - nonce: 1, + nonce: 1u64.into(), consumers: 0, providers: 1, sufficients: 0, @@ -153,20 +153,20 @@ mod tests { let len = 0_usize; // stale assert_noop!( - CheckNonce::(0).validate(&1, CALL, &info, len), + CheckNonce::(0u64.into()).validate(&1, CALL, &info, len), InvalidTransaction::Stale ); assert_noop!( - CheckNonce::(0).pre_dispatch(&1, CALL, &info, len), + CheckNonce::(0u64.into()).pre_dispatch(&1, CALL, &info, len), InvalidTransaction::Stale ); // correct - assert_ok!(CheckNonce::(1).validate(&1, CALL, &info, len)); - assert_ok!(CheckNonce::(1).pre_dispatch(&1, CALL, &info, len)); + assert_ok!(CheckNonce::(1u64.into()).validate(&1, CALL, &info, len)); + assert_ok!(CheckNonce::(1u64.into()).pre_dispatch(&1, CALL, &info, len)); // future - assert_ok!(CheckNonce::(5).validate(&1, CALL, &info, len)); + assert_ok!(CheckNonce::(5u64.into()).validate(&1, CALL, &info, len)); assert_noop!( - CheckNonce::(5).pre_dispatch(&1, CALL, &info, len), + CheckNonce::(5u64.into()).pre_dispatch(&1, CALL, &info, len), InvalidTransaction::Future ); }) @@ -178,7 +178,7 @@ mod tests { crate::Account::::insert( 2, crate::AccountInfo { - nonce: 1, + nonce: 1u64.into(), consumers: 0, providers: 1, sufficients: 0, @@ -188,7 +188,7 @@ mod tests { crate::Account::::insert( 3, crate::AccountInfo { - nonce: 1, + nonce: 1u64.into(), consumers: 0, providers: 0, sufficients: 1, @@ -199,19 +199,19 @@ mod tests { let len = 0_usize; // Both providers and sufficients zero assert_noop!( - CheckNonce::(1).validate(&1, CALL, &info, len), + CheckNonce::(1u64.into()).validate(&1, CALL, &info, len), InvalidTransaction::Payment ); assert_noop!( - CheckNonce::(1).pre_dispatch(&1, CALL, &info, len), + CheckNonce::(1u64.into()).pre_dispatch(&1, CALL, &info, len), InvalidTransaction::Payment ); // Non-zero providers - assert_ok!(CheckNonce::(1).validate(&2, CALL, &info, len)); - assert_ok!(CheckNonce::(1).pre_dispatch(&2, CALL, &info, len)); + assert_ok!(CheckNonce::(1u64.into()).validate(&2, CALL, &info, len)); + assert_ok!(CheckNonce::(1u64.into()).pre_dispatch(&2, CALL, &info, len)); // Non-zero sufficients - assert_ok!(CheckNonce::(1).validate(&3, CALL, &info, len)); - assert_ok!(CheckNonce::(1).pre_dispatch(&3, CALL, &info, len)); + assert_ok!(CheckNonce::(1u64.into()).validate(&3, CALL, &info, len)); + assert_ok!(CheckNonce::(1u64.into()).pre_dispatch(&3, CALL, &info, len)); }) } } diff --git a/substrate/frame/system/src/mock.rs b/substrate/frame/system/src/mock.rs index e1959e572e99..fff848b3b0e5 100644 --- a/substrate/frame/system/src/mock.rs +++ b/substrate/frame/system/src/mock.rs @@ -17,7 +17,7 @@ use crate::{self as frame_system, *}; use frame_support::{derive_impl, parameter_types}; -use sp_runtime::{BuildStorage, Perbill}; +use sp_runtime::{type_with_default::TypeWithDefault, BuildStorage, Perbill}; type Block = mocking::MockBlock; @@ -78,6 +78,14 @@ impl OnKilledAccount for RecordKilled { } } +#[derive(Debug, TypeInfo)] +pub struct DefaultNonceProvider; +impl Get for DefaultNonceProvider { + fn get() -> u64 { + System::block_number() + } +} + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl Config for Test { type BlockWeights = RuntimeBlockWeights; @@ -87,6 +95,7 @@ impl Config for Test { type AccountData = u32; type OnKilledAccount = RecordKilled; type MultiBlockMigrator = MockedMigrator; + type Nonce = TypeWithDefault; } parameter_types! { diff --git a/substrate/frame/system/src/tests.rs b/substrate/frame/system/src/tests.rs index b889b5ca046e..4eff53789d08 100644 --- a/substrate/frame/system/src/tests.rs +++ b/substrate/frame/system/src/tests.rs @@ -102,7 +102,13 @@ fn stored_map_works() { assert_eq!( Account::::get(0), - AccountInfo { nonce: 0, providers: 1, consumers: 0, sufficients: 0, data: 42 } + AccountInfo { + nonce: 0u64.into(), + providers: 1, + consumers: 0, + sufficients: 0, + data: 42 + } ); assert_ok!(System::inc_consumers(&0)); @@ -126,26 +132,26 @@ fn provider_ref_handover_to_self_sufficient_ref_works() { new_test_ext().execute_with(|| { assert_eq!(System::inc_providers(&0), IncRefStatus::Created); System::inc_account_nonce(&0); - assert_eq!(System::account_nonce(&0), 1); + assert_eq!(System::account_nonce(&0), 1u64.into()); // a second reference coming and going doesn't change anything. assert_eq!(System::inc_sufficients(&0), IncRefStatus::Existed); assert_eq!(System::dec_sufficients(&0), DecRefStatus::Exists); - assert_eq!(System::account_nonce(&0), 1); + assert_eq!(System::account_nonce(&0), 1u64.into()); // a provider reference coming and going doesn't change anything. assert_eq!(System::inc_providers(&0), IncRefStatus::Existed); assert_eq!(System::dec_providers(&0).unwrap(), DecRefStatus::Exists); - assert_eq!(System::account_nonce(&0), 1); + assert_eq!(System::account_nonce(&0), 1u64.into()); // decreasing the providers with a self-sufficient present should not delete the account assert_eq!(System::inc_sufficients(&0), IncRefStatus::Existed); assert_eq!(System::dec_providers(&0).unwrap(), DecRefStatus::Exists); - assert_eq!(System::account_nonce(&0), 1); + assert_eq!(System::account_nonce(&0), 1u64.into()); // decreasing the sufficients should delete the account assert_eq!(System::dec_sufficients(&0), DecRefStatus::Reaped); - assert_eq!(System::account_nonce(&0), 0); + assert_eq!(System::account_nonce(&0), 0u64.into()); }); } @@ -154,26 +160,26 @@ fn self_sufficient_ref_handover_to_provider_ref_works() { new_test_ext().execute_with(|| { assert_eq!(System::inc_sufficients(&0), IncRefStatus::Created); System::inc_account_nonce(&0); - assert_eq!(System::account_nonce(&0), 1); + assert_eq!(System::account_nonce(&0), 1u64.into()); // a second reference coming and going doesn't change anything. assert_eq!(System::inc_providers(&0), IncRefStatus::Existed); assert_eq!(System::dec_providers(&0).unwrap(), DecRefStatus::Exists); - assert_eq!(System::account_nonce(&0), 1); + assert_eq!(System::account_nonce(&0), 1u64.into()); // a sufficient reference coming and going doesn't change anything. assert_eq!(System::inc_sufficients(&0), IncRefStatus::Existed); assert_eq!(System::dec_sufficients(&0), DecRefStatus::Exists); - assert_eq!(System::account_nonce(&0), 1); + assert_eq!(System::account_nonce(&0), 1u64.into()); // decreasing the sufficients with a provider present should not delete the account assert_eq!(System::inc_providers(&0), IncRefStatus::Existed); assert_eq!(System::dec_sufficients(&0), DecRefStatus::Exists); - assert_eq!(System::account_nonce(&0), 1); + assert_eq!(System::account_nonce(&0), 1u64.into()); // decreasing the providers should delete the account assert_eq!(System::dec_providers(&0).unwrap(), DecRefStatus::Reaped); - assert_eq!(System::account_nonce(&0), 0); + assert_eq!(System::account_nonce(&0), 0u64.into()); }); } @@ -182,7 +188,7 @@ fn sufficient_cannot_support_consumer() { new_test_ext().execute_with(|| { assert_eq!(System::inc_sufficients(&0), IncRefStatus::Created); System::inc_account_nonce(&0); - assert_eq!(System::account_nonce(&0), 1); + assert_eq!(System::account_nonce(&0), 1u64.into()); assert_noop!(System::inc_consumers(&0), DispatchError::NoProviders); assert_eq!(System::inc_providers(&0), IncRefStatus::Existed); @@ -198,18 +204,18 @@ fn provider_required_to_support_consumer() { assert_eq!(System::inc_providers(&0), IncRefStatus::Created); System::inc_account_nonce(&0); - assert_eq!(System::account_nonce(&0), 1); + assert_eq!(System::account_nonce(&0), 1u64.into()); assert_eq!(System::inc_providers(&0), IncRefStatus::Existed); assert_eq!(System::dec_providers(&0).unwrap(), DecRefStatus::Exists); - assert_eq!(System::account_nonce(&0), 1); + assert_eq!(System::account_nonce(&0), 1u64.into()); assert_ok!(System::inc_consumers(&0)); assert_noop!(System::dec_providers(&0), DispatchError::ConsumerRemaining); System::dec_consumers(&0); assert_eq!(System::dec_providers(&0).unwrap(), DecRefStatus::Reaped); - assert_eq!(System::account_nonce(&0), 0); + assert_eq!(System::account_nonce(&0), 0u64.into()); }); } @@ -858,3 +864,20 @@ fn last_runtime_upgrade_spec_version_usage() { } } } + +#[test] +fn test_default_account_nonce() { + new_test_ext().execute_with(|| { + System::set_block_number(2); + assert_eq!(System::account_nonce(&1), 2u64.into()); + + System::inc_account_nonce(&1); + assert_eq!(System::account_nonce(&1), 3u64.into()); + + System::set_block_number(5); + assert_eq!(System::account_nonce(&1), 3u64.into()); + + Account::::remove(&1); + assert_eq!(System::account_nonce(&1), 5u64.into()); + }); +} diff --git a/substrate/primitives/runtime/Cargo.toml b/substrate/primitives/runtime/Cargo.toml index fb5fd60fbbfd..0389c9f5b2f4 100644 --- a/substrate/primitives/runtime/Cargo.toml +++ b/substrate/primitives/runtime/Cargo.toml @@ -22,6 +22,7 @@ either = { version = "1.5", default-features = false } hash256-std-hasher = { version = "0.15.2", default-features = false } impl-trait-for-tuples = "0.2.2" log = { workspace = true } +num-traits = { version = "0.2.17", default-features = false } paste = "1.0" rand = { version = "0.8.5", optional = true } scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } @@ -54,6 +55,7 @@ std = [ "either/use_std", "hash256-std-hasher/std", "log/std", + "num-traits/std", "rand", "scale-info/std", "serde/std", diff --git a/substrate/primitives/runtime/src/lib.rs b/substrate/primitives/runtime/src/lib.rs index 44bf3c969e54..e4e6b98ff77c 100644 --- a/substrate/primitives/runtime/src/lib.rs +++ b/substrate/primitives/runtime/src/lib.rs @@ -91,6 +91,7 @@ mod runtime_string; pub mod testing; pub mod traits; pub mod transaction_validity; +pub mod type_with_default; pub use crate::runtime_string::*; diff --git a/substrate/primitives/runtime/src/type_with_default.rs b/substrate/primitives/runtime/src/type_with_default.rs new file mode 100644 index 000000000000..1465393640dc --- /dev/null +++ b/substrate/primitives/runtime/src/type_with_default.rs @@ -0,0 +1,506 @@ +// This file is part of Substrate. + +// Copyright (C) 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. + +//! Provides a type that wraps another type and provides a default value. + +use crate::traits::{Bounded, One, Zero}; +use codec::{Compact, CompactAs, Decode, Encode, HasCompact, MaxEncodedLen}; +use core::{ + fmt::Display, + marker::PhantomData, + ops::{ + Add, AddAssign, BitAnd, BitOr, BitXor, Deref, Div, DivAssign, Mul, MulAssign, Not, Rem, + RemAssign, Shl, Shr, Sub, SubAssign, + }, +}; +use num_traits::{ + CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, CheckedSub, + Num, NumCast, PrimInt, Saturating, ToPrimitive, +}; +use scale_info::TypeInfo; +use sp_core::Get; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// A type that wraps another type and provides a default value. +/// +/// Passes through arithmetical and many other operations to the inner value. +#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct TypeWithDefault>(T, PhantomData); + +impl> TypeWithDefault { + fn new(value: T) -> Self { + Self(value, PhantomData) + } +} + +impl> Clone for TypeWithDefault { + fn clone(&self) -> Self { + Self(self.0.clone(), PhantomData) + } +} + +impl> Copy for TypeWithDefault {} + +impl> PartialEq for TypeWithDefault { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl> Eq for TypeWithDefault {} + +impl> PartialOrd for TypeWithDefault { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } +} + +impl> Ord for TypeWithDefault { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.0.cmp(&other.0) + } +} + +impl> Deref for TypeWithDefault { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl> Default for TypeWithDefault { + fn default() -> Self { + Self::new(D::get()) + } +} + +impl, D: Get> From for TypeWithDefault { + fn from(value: u16) -> Self { + Self::new(value.into()) + } +} + +impl, D: Get> From for TypeWithDefault { + fn from(value: u32) -> Self { + Self::new(value.into()) + } +} + +impl, D: Get> From for TypeWithDefault { + fn from(value: u64) -> Self { + Self::new(value.into()) + } +} + +impl> CheckedNeg for TypeWithDefault { + fn checked_neg(&self) -> Option { + self.0.checked_neg().map(Self::new) + } +} + +impl> CheckedRem for TypeWithDefault { + fn checked_rem(&self, rhs: &Self) -> Option { + self.0.checked_rem(&rhs.0).map(Self::new) + } +} + +impl> CheckedShr for TypeWithDefault { + fn checked_shr(&self, n: u32) -> Option { + self.0.checked_shr(n).map(Self::new) + } +} + +impl> CheckedShl for TypeWithDefault { + fn checked_shl(&self, n: u32) -> Option { + self.0.checked_shl(n).map(Self::new) + } +} + +impl, D: Get> Rem for TypeWithDefault { + type Output = Self; + fn rem(self, rhs: Self) -> Self { + Self::new(self.0 % rhs.0) + } +} + +impl, D: Get> Rem for TypeWithDefault { + type Output = Self; + fn rem(self, rhs: u32) -> Self { + Self::new(self.0 % (rhs.into())) + } +} + +impl, D: Get> Shr for TypeWithDefault { + type Output = Self; + fn shr(self, rhs: u32) -> Self { + Self::new(self.0 >> rhs) + } +} + +impl, D: Get> Shr for TypeWithDefault { + type Output = Self; + fn shr(self, rhs: usize) -> Self { + Self::new(self.0 >> rhs) + } +} + +impl, D: Get> Shl for TypeWithDefault { + type Output = Self; + fn shl(self, rhs: u32) -> Self { + Self::new(self.0 << rhs) + } +} + +impl, D: Get> Shl for TypeWithDefault { + type Output = Self; + fn shl(self, rhs: usize) -> Self { + Self::new(self.0 << rhs) + } +} + +impl> RemAssign for TypeWithDefault { + fn rem_assign(&mut self, rhs: Self) { + self.0 %= rhs.0 + } +} + +impl> DivAssign for TypeWithDefault { + fn div_assign(&mut self, rhs: Self) { + self.0 /= rhs.0 + } +} + +impl> MulAssign for TypeWithDefault { + fn mul_assign(&mut self, rhs: Self) { + self.0 *= rhs.0 + } +} + +impl> SubAssign for TypeWithDefault { + fn sub_assign(&mut self, rhs: Self) { + self.0 -= rhs.0 + } +} + +impl> AddAssign for TypeWithDefault { + fn add_assign(&mut self, rhs: Self) { + self.0 += rhs.0 + } +} + +impl, D: Get> From for TypeWithDefault { + fn from(value: u8) -> Self { + Self::new(value.into()) + } +} + +impl> Display for TypeWithDefault { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl, D: Get> TryFrom for TypeWithDefault { + type Error = >::Error; + fn try_from(n: u128) -> Result, Self::Error> { + T::try_from(n).map(Self::new) + } +} + +impl, D: Get> TryFrom for TypeWithDefault { + type Error = >::Error; + fn try_from(n: usize) -> Result, Self::Error> { + T::try_from(n).map(Self::new) + } +} + +impl, D: Get> TryFrom> for u8 { + type Error = >::Error; + fn try_from(value: TypeWithDefault) -> Result { + value.0.try_into() + } +} + +impl, D: Get> TryFrom> for u16 { + type Error = >::Error; + fn try_from(value: TypeWithDefault) -> Result { + value.0.try_into() + } +} + +impl, D: Get> TryFrom> for u32 { + type Error = >::Error; + fn try_from(value: TypeWithDefault) -> Result { + value.0.try_into() + } +} + +impl, D: Get> TryFrom> for u64 { + type Error = >::Error; + fn try_from(value: TypeWithDefault) -> Result { + value.0.try_into() + } +} + +impl, D: Get> TryFrom> for u128 { + type Error = >::Error; + fn try_from(value: TypeWithDefault) -> Result { + value.0.try_into() + } +} + +impl, D: Get> TryFrom> for usize { + type Error = >::Error; + fn try_from(value: TypeWithDefault) -> Result { + value.0.try_into() + } +} + +impl> Zero for TypeWithDefault { + fn zero() -> Self { + Self::new(T::zero()) + } + + fn is_zero(&self) -> bool { + self.0 == T::zero() + } +} + +impl> Bounded for TypeWithDefault { + fn min_value() -> Self { + Self::new(T::min_value()) + } + + fn max_value() -> Self { + Self::new(T::max_value()) + } +} + +impl> PrimInt for TypeWithDefault { + fn count_ones(self) -> u32 { + self.0.count_ones() + } + + fn leading_zeros(self) -> u32 { + self.0.leading_zeros() + } + + fn trailing_zeros(self) -> u32 { + self.0.trailing_zeros() + } + + fn rotate_left(self, n: u32) -> Self { + Self::new(self.0.rotate_left(n)) + } + + fn rotate_right(self, n: u32) -> Self { + Self::new(self.0.rotate_right(n)) + } + + fn swap_bytes(self) -> Self { + Self::new(self.0.swap_bytes()) + } + + fn from_be(x: Self) -> Self { + Self::new(T::from_be(x.0)) + } + + fn from_le(x: Self) -> Self { + Self::new(T::from_le(x.0)) + } + + fn to_be(self) -> Self { + Self::new(self.0.to_be()) + } + + fn to_le(self) -> Self { + Self::new(self.0.to_le()) + } + + fn count_zeros(self) -> u32 { + self.0.count_zeros() + } + + fn signed_shl(self, n: u32) -> Self { + Self::new(self.0.signed_shl(n)) + } + + fn signed_shr(self, n: u32) -> Self { + Self::new(self.0.signed_shr(n)) + } + + fn unsigned_shl(self, n: u32) -> Self { + Self::new(self.0.unsigned_shl(n)) + } + + fn unsigned_shr(self, n: u32) -> Self { + Self::new(self.0.unsigned_shr(n)) + } + + fn pow(self, exp: u32) -> Self { + Self::new(self.0.pow(exp)) + } +} + +impl> Saturating for TypeWithDefault { + fn saturating_add(self, rhs: Self) -> Self { + Self::new(self.0.saturating_add(rhs.0)) + } + + fn saturating_sub(self, rhs: Self) -> Self { + Self::new(self.0.saturating_sub(rhs.0)) + } +} + +impl, D: Get> Div for TypeWithDefault { + type Output = Self; + fn div(self, rhs: Self) -> Self { + Self::new(self.0 / rhs.0) + } +} + +impl, D: Get> Mul for TypeWithDefault { + type Output = Self; + fn mul(self, rhs: Self) -> Self { + Self::new(self.0 * rhs.0) + } +} + +impl> CheckedDiv for TypeWithDefault { + fn checked_div(&self, rhs: &Self) -> Option { + self.0.checked_div(&rhs.0).map(Self::new) + } +} + +impl> CheckedMul for TypeWithDefault { + fn checked_mul(&self, rhs: &Self) -> Option { + self.0.checked_mul(&rhs.0).map(Self::new) + } +} + +impl, D: Get> Sub for TypeWithDefault { + type Output = Self; + fn sub(self, rhs: Self) -> Self { + Self::new(self.0 - rhs.0) + } +} + +impl> CheckedSub for TypeWithDefault { + fn checked_sub(&self, rhs: &Self) -> Option { + self.0.checked_sub(&rhs.0).map(Self::new) + } +} + +impl, D: Get> Add for TypeWithDefault { + type Output = Self; + fn add(self, rhs: Self) -> Self { + Self::new(self.0 + rhs.0) + } +} + +impl> CheckedAdd for TypeWithDefault { + fn checked_add(&self, rhs: &Self) -> Option { + self.0.checked_add(&rhs.0).map(Self::new) + } +} + +impl, D: Get> BitAnd for TypeWithDefault { + type Output = Self; + fn bitand(self, rhs: Self) -> Self { + Self::new(self.0 & rhs.0) + } +} + +impl, D: Get> BitOr for TypeWithDefault { + type Output = Self; + fn bitor(self, rhs: Self) -> Self { + Self::new(self.0 | rhs.0) + } +} + +impl, D: Get> BitXor for TypeWithDefault { + type Output = Self; + fn bitxor(self, rhs: Self) -> Self { + Self::new(self.0 ^ rhs.0) + } +} + +impl> One for TypeWithDefault { + fn one() -> Self { + Self::new(T::one()) + } +} + +impl, D: Get> Not for TypeWithDefault { + type Output = Self; + fn not(self) -> Self { + Self::new(self.0.not()) + } +} + +impl> NumCast for TypeWithDefault { + fn from(n: P) -> Option { + ::from(n).map_or(None, |n| Some(Self::new(n))) + } +} + +impl> Num for TypeWithDefault { + type FromStrRadixErr = ::FromStrRadixErr; + + fn from_str_radix(s: &str, radix: u32) -> Result { + T::from_str_radix(s, radix).map(Self::new) + } +} + +impl> ToPrimitive for TypeWithDefault { + fn to_i64(&self) -> Option { + self.0.to_i64() + } + + fn to_u64(&self) -> Option { + self.0.to_u64() + } + + fn to_i128(&self) -> Option { + self.0.to_i128() + } + + fn to_u128(&self) -> Option { + self.0.to_u128() + } +} + +impl> From>> for TypeWithDefault { + fn from(c: Compact>) -> Self { + c.0 + } +} + +impl> CompactAs for TypeWithDefault { + type As = T; + + fn encode_as(&self) -> &Self::As { + &self.0 + } + + fn decode_from(val: Self::As) -> Result { + Ok(Self::new(val)) + } +}