Repository: chainx-org/ChainX (language: rust)

Issue:
Integrate Proxy module
We don't have the Stash and Controller account concepts in our Staking, with the Proxy module we can expect the same security and it's more flexible, you can keep an account in cold storage and perform some operations using the other account on behalf of the cold account.

Ref: https://wiki.polkadot.network/docs/en/learn-proxies


Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 5cc12f7b0..75eac5358 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -842,6 +842,7 @@ dependencies = [
  "pallet-membership",
  "pallet-multisig",
  "pallet-offences",
+ "pallet-proxy",
  "pallet-randomness-collective-flip",
  "pallet-scheduler",
  "pallet-session",
@@ -1375,6 +1376,7 @@ dependencies = [
  "pallet-membership",
  "pallet-multisig",
  "pallet-offences",
+ "pallet-proxy",
  "pallet-randomness-collective-flip",
  "pallet-scheduler",
  "pallet-session",
@@ -3726,6 +3728,7 @@ dependencies = [
  "pallet-membership",
  "pallet-multisig",
  "pallet-offences",
+ "pallet-proxy",
  "pallet-randomness-collective-flip",
  "pallet-scheduler",
  "pallet-session",
@@ -4503,6 +4506,21 @@ dependencies = [
  "sp-std",
 ]
 
+[[package]]
+name = "pallet-proxy"
+version = "2.0.1"
+source = "git+https://github.com/paritytech/substrate?rev=11ace4ef8b2ad176293ad6db2b3dd795befd2c79#11ace4ef8b2ad176293ad6db2b3dd795befd2c79"
+dependencies = [
+ "frame-support",
+ "frame-system",
+ "parity-scale-codec",
+ "serde",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+]
+
 [[package]]
 name = "pallet-randomness-collective-flip"
 version = "2.0.0"
diff --git a/Cargo.toml b/Cargo.toml
index 911431813..150e8a08e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -85,6 +85,7 @@ pallet-indices = { git = "https://github.com/paritytech/substrate", rev = "11ace
 pallet-membership = { git = "https://github.com/paritytech/substrate", rev = "11ace4ef8b2ad176293ad6db2b3dd795befd2c79" }
 pallet-multisig = { git = "https://github.com/paritytech/substrate", rev = "11ace4ef8b2ad176293ad6db2b3dd795befd2c79" }
 pallet-offences = { git = "https://github.com/paritytech/substrate", rev = "11ace4ef8b2ad176293ad6db2b3dd795befd2c79" }
+pallet-proxy = { git = "https://github.com/paritytech/substrate", rev = "11ace4ef8b2ad176293ad6db2b3dd795befd2c79" }
 pallet-randomness-collective-flip = { git = "https://github.com/paritytech/substrate", rev = "11ace4ef8b2ad176293ad6db2b3dd795befd2c79" }
 pallet-scheduler = { git = "https://github.com/paritytech/substrate", rev = "11ace4ef8b2ad176293ad6db2b3dd795befd2c79" }
 pallet-session = { git = "https://github.com/paritytech/substrate", rev = "11ace4ef8b2ad176293ad6db2b3dd795befd2c79" }
diff --git a/runtime/chainx/Cargo.toml b/runtime/chainx/Cargo.toml
index 97bf929c5..f07057fa9 100644
--- a/runtime/chainx/Cargo.toml
+++ b/runtime/chainx/Cargo.toml
@@ -48,6 +48,7 @@ pallet-indices = { version = "2.0.0", default-features = false }
 pallet-membership = { version = "2.0.0", default-features = false }
 pallet-multisig = { version = "2.0.0", default-features = false }
 pallet-offences = { version = "2.0.0", default-features = false }
+pallet-proxy = { version = "2.0.0", default-features = false }
 pallet-randomness-collective-flip = { version = "2.0.0", default-features = false }
 pallet-scheduler = { version = "2.0.0", default-features = false }
 pallet-session = { version = "2.0.0", default-features = false, features = ["historical"] }
@@ -135,6 +136,7 @@ std = [
     "pallet-membership/std",
     "pallet-multisig/std",
     "pallet-offences/std",
+    "pallet-proxy/std",
     "pallet-randomness-collective-flip/std",
     "pallet-scheduler/std",
     "pallet-session/std",
diff --git a/runtime/chainx/src/lib.rs b/runtime/chainx/src/lib.rs
index 283a8e6f4..475768993 100644
--- a/runtime/chainx/src/lib.rs
+++ b/runtime/chainx/src/lib.rs
@@ -10,7 +10,7 @@
 #[cfg(feature = "std")]
 include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
 
-use codec::Encode;
+use codec::{Decode, Encode};
 use static_assertions::const_assert;
 
 use sp_api::impl_runtime_apis;
@@ -31,7 +31,7 @@ use sp_runtime::{
         InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity,
         TransactionValidityError, ValidTransaction,
     },
-    ApplyExtrinsicResult, DispatchError, ModuleId, Perbill, Percent, Permill,
+    ApplyExtrinsicResult, DispatchError, ModuleId, Perbill, Percent, Permill, RuntimeDebug,
 };
 use sp_std::{collections::btree_map::BTreeMap, prelude::*};
 #[cfg(feature = "std")]
@@ -104,7 +104,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
     spec_name: create_runtime_str!("chainx"),
     impl_name: create_runtime_str!("chainx-net"),
     authoring_version: 1,
-    spec_version: 9,
+    spec_version: 10,
     impl_version: 1,
     apis: RUNTIME_API_VERSIONS,
     transaction_version: 1,
@@ -758,6 +758,115 @@ impl pallet_identity::Trait for Runtime {
     type WeightInfo = pallet_identity::weights::SubstrateWeight<Runtime>;
 }
 
+parameter_types! {
+    // One storage item; key size 32, value size 8; .
+    pub const ProxyDepositBase: Balance = deposit(1, 8);
+    // Additional storage item size of 33 bytes.
+    pub const ProxyDepositFactor: Balance = deposit(0, 33);
+    pub const MaxProxies: u16 = 32;
+    pub const AnnouncementDepositBase: Balance = deposit(1, 8);
+    pub const AnnouncementDepositFactor: Balance = deposit(0, 66);
+    pub const MaxPending: u16 = 32;
+}
+
+/// The type used to represent the kinds of proxying allowed.
+#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, RuntimeDebug)]
+pub enum ProxyType {
+    Any = 0,
+    NonTransfer = 1,
+    Governance = 2,
+    Staking = 3,
+    IdentityJudgement = 4,
+    CancelProxy = 5,
+}
+
+impl Default for ProxyType {
+    fn default() -> Self {
+        Self::Any
+    }
+}
+
+impl InstanceFilter<Call> for ProxyType {
+    fn filter(&self, c: &Call) -> bool {
+        match self {
+            ProxyType::Any => true,
+            ProxyType::NonTransfer => matches!(
+                c,
+                Call::System(..)
+                    | Call::Scheduler(..)
+                    | Call::Babe(..)
+                    | Call::Timestamp(..)
+                    | Call::Indices(pallet_indices::Call::claim(..))
+                    | Call::Indices(pallet_indices::Call::free(..))
+                    | Call::Indices(pallet_indices::Call::freeze(..))
+                    // Specifically omitting Indices `transfer`, `force_transfer`
+                    // Specifically omitting the entire Balances pallet
+                    | Call::Authorship(..)
+                    | Call::XStaking(..)
+                    | Call::Offences(..)
+                    | Call::Session(..)
+                    | Call::Grandpa(..)
+                    | Call::ImOnline(..)
+                    | Call::AuthorityDiscovery(..)
+                    | Call::Democracy(..)
+                    | Call::Council(..)
+                    | Call::TechnicalCommittee(..)
+                    | Call::Elections(..)
+                    | Call::TechnicalMembership(..)
+                    | Call::Treasury(..)
+                    | Call::Utility(..)
+                    | Call::Identity(..)
+                    | Call::Proxy(..)
+                    | Call::Multisig(..)
+            ),
+            ProxyType::Governance => matches!(
+                c,
+                Call::Democracy(..)
+                    | Call::Council(..)
+                    | Call::TechnicalCommittee(..)
+                    | Call::Elections(..)
+                    | Call::Treasury(..)
+                    | Call::Utility(..)
+            ),
+            ProxyType::Staking => matches!(
+                c,
+                Call::XStaking(..) | Call::Session(..) | Call::Utility(..)
+            ),
+            ProxyType::IdentityJudgement => matches!(
+                c,
+                Call::Identity(pallet_identity::Call::provide_judgement(..)) | Call::Utility(..)
+            ),
+            ProxyType::CancelProxy => {
+                matches!(c, Call::Proxy(pallet_proxy::Call::reject_announcement(..)))
+            }
+        }
+    }
+    fn is_superset(&self, o: &Self) -> bool {
+        match (self, o) {
+            (x, y) if x == y => true,
+            (ProxyType::Any, _) => true,
+            (_, ProxyType::Any) => false,
+            (ProxyType::NonTransfer, _) => true,
+            _ => false,
+        }
+    }
+}
+
+impl pallet_proxy::Trait for Runtime {
+    type Event = Event;
+    type Call = Call;
+    type Currency = Balances;
+    type ProxyType = ProxyType;
+    type ProxyDepositBase = ProxyDepositBase;
+    type ProxyDepositFactor = ProxyDepositFactor;
+    type MaxProxies = MaxProxies;
+    type WeightInfo = pallet_proxy::weights::SubstrateWeight<Runtime>;
+    type MaxPending = MaxPending;
+    type CallHasher = BlakeTwo256;
+    type AnnouncementDepositBase = AnnouncementDepositBase;
+    type AnnouncementDepositFactor = AnnouncementDepositFactor;
+}
+
 ///////////////////////////////////////////
 // orml
 ///////////////////////////////////////////
@@ -956,6 +1065,8 @@ construct_runtime!(
         // It might be possible to merge this module into pallet_transaction_payment in future, thus
         // we put it at the end for keeping the extrinsic ordering.
         XTransactionFee: xpallet_transaction_fee::{Module, Event<T>} = 35,
+
+        Proxy: pallet_proxy::{Module, Call, Storage, Event<T>} = 37,
     }
 );
 
diff --git a/runtime/dev/Cargo.toml b/runtime/dev/Cargo.toml
index 7cfdc6c81..3e2198c4e 100644
--- a/runtime/dev/Cargo.toml
+++ b/runtime/dev/Cargo.toml
@@ -48,6 +48,7 @@ pallet-indices = { version = "2.0.0", default-features = false }
 pallet-membership = { version = "2.0.0", default-features = false }
 pallet-multisig = { version = "2.0.0", default-features = false }
 pallet-offences = { version = "2.0.0", default-features = false }
+pallet-proxy = { version = "2.0.0", default-features = false }
 pallet-randomness-collective-flip = { version = "2.0.0", default-features = false }
 pallet-scheduler = { version = "2.0.0", default-features = false }
 pallet-session = { version = "2.0.0", default-features = false, features = ["historical"] }
@@ -136,6 +137,7 @@ std = [
     "pallet-membership/std",
     "pallet-multisig/std",
     "pallet-offences/std",
+    "pallet-proxy/std",
     "pallet-randomness-collective-flip/std",
     "pallet-scheduler/std",
     "pallet-session/std",
diff --git a/runtime/dev/src/lib.rs b/runtime/dev/src/lib.rs
index dc6af19c3..c5e26b460 100644
--- a/runtime/dev/src/lib.rs
+++ b/runtime/dev/src/lib.rs
@@ -10,7 +10,7 @@
 #[cfg(feature = "std")]
 include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
 
-use codec::Encode;
+use codec::{Decode, Encode};
 use static_assertions::const_assert;
 
 use sp_api::impl_runtime_apis;
@@ -31,7 +31,7 @@ use sp_runtime::{
         InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity,
         TransactionValidityError, ValidTransaction,
     },
-    ApplyExtrinsicResult, DispatchError, ModuleId, Perbill, Percent, Permill,
+    ApplyExtrinsicResult, DispatchError, ModuleId, Perbill, Percent, Permill, RuntimeDebug,
 };
 use sp_std::{collections::btree_map::BTreeMap, prelude::*};
 #[cfg(feature = "std")]
@@ -761,6 +761,115 @@ impl pallet_identity::Trait for Runtime {
     type WeightInfo = pallet_identity::weights::SubstrateWeight<Runtime>;
 }
 
+parameter_types! {
+    // One storage item; key size 32, value size 8; .
+    pub const ProxyDepositBase: Balance = deposit(1, 8);
+    // Additional storage item size of 33 bytes.
+    pub const ProxyDepositFactor: Balance = deposit(0, 33);
+    pub const MaxProxies: u16 = 32;
+    pub const AnnouncementDepositBase: Balance = deposit(1, 8);
+    pub const AnnouncementDepositFactor: Balance = deposit(0, 66);
+    pub const MaxPending: u16 = 32;
+}
+
+/// The type used to represent the kinds of proxying allowed.
+#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, RuntimeDebug)]
+pub enum ProxyType {
+    Any = 0,
+    NonTransfer = 1,
+    Governance = 2,
+    Staking = 3,
+    IdentityJudgement = 4,
+    CancelProxy = 5,
+}
+
+impl Default for ProxyType {
+    fn default() -> Self {
+        Self::Any
+    }
+}
+
+impl InstanceFilter<Call> for ProxyType {
+    fn filter(&self, c: &Call) -> bool {
+        match self {
+            ProxyType::Any => true,
+            ProxyType::NonTransfer => matches!(
+                c,
+                Call::System(..)
+                    | Call::Scheduler(..)
+                    | Call::Babe(..)
+                    | Call::Timestamp(..)
+                    | Call::Indices(pallet_indices::Call::claim(..))
+                    | Call::Indices(pallet_indices::Call::free(..))
+                    | Call::Indices(pallet_indices::Call::freeze(..))
+                    // Specifically omitting Indices `transfer`, `force_transfer`
+                    // Specifically omitting the entire Balances pallet
+                    | Call::Authorship(..)
+                    | Call::XStaking(..)
+                    | Call::Offences(..)
+                    | Call::Session(..)
+                    | Call::Grandpa(..)
+                    | Call::ImOnline(..)
+                    | Call::AuthorityDiscovery(..)
+                    | Call::Democracy(..)
+                    | Call::Council(..)
+                    | Call::TechnicalCommittee(..)
+                    | Call::Elections(..)
+                    | Call::TechnicalMembership(..)
+                    | Call::Treasury(..)
+                    | Call::Utility(..)
+                    | Call::Identity(..)
+                    | Call::Proxy(..)
+                    | Call::Multisig(..)
+            ),
+            ProxyType::Governance => matches!(
+                c,
+                Call::Democracy(..)
+                    | Call::Council(..)
+                    | Call::TechnicalCommittee(..)
+                    | Call::Elections(..)
+                    | Call::Treasury(..)
+                    | Call::Utility(..)
+            ),
+            ProxyType::Staking => matches!(
+                c,
+                Call::XStaking(..) | Call::Session(..) | Call::Utility(..)
+            ),
+            ProxyType::IdentityJudgement => matches!(
+                c,
+                Call::Identity(pallet_identity::Call::provide_judgement(..)) | Call::Utility(..)
+            ),
+            ProxyType::CancelProxy => {
+                matches!(c, Call::Proxy(pallet_proxy::Call::reject_announcement(..)))
+            }
+        }
+    }
+    fn is_superset(&self, o: &Self) -> bool {
+        match (self, o) {
+            (x, y) if x == y => true,
+            (ProxyType::Any, _) => true,
+            (_, ProxyType::Any) => false,
+            (ProxyType::NonTransfer, _) => true,
+            _ => false,
+        }
+    }
+}
+
+impl pallet_proxy::Trait for Runtime {
+    type Event = Event;
+    type Call = Call;
+    type Currency = Balances;
+    type ProxyType = ProxyType;
+    type ProxyDepositBase = ProxyDepositBase;
+    type ProxyDepositFactor = ProxyDepositFactor;
+    type MaxProxies = MaxProxies;
+    type WeightInfo = pallet_proxy::weights::SubstrateWeight<Runtime>;
+    type MaxPending = MaxPending;
+    type CallHasher = BlakeTwo256;
+    type AnnouncementDepositBase = AnnouncementDepositBase;
+    type AnnouncementDepositFactor = AnnouncementDepositFactor;
+}
+
 ///////////////////////////////////////////
 // orml
 ///////////////////////////////////////////
@@ -961,6 +1070,8 @@ construct_runtime!(
 
         // Put Sudo last so that the extrinsic ordering stays the same once it's removed.
         Sudo: pallet_sudo::{Module, Call, Config<T>, Storage, Event<T>},
+
+        Proxy: pallet_proxy::{Module, Call, Storage, Event<T>},
     }
 );
 
diff --git a/runtime/malan/Cargo.toml b/runtime/malan/Cargo.toml
index 3bed9877c..3dedd8b07 100644
--- a/runtime/malan/Cargo.toml
+++ b/runtime/malan/Cargo.toml
@@ -48,6 +48,7 @@ pallet-indices = { version = "2.0.0", default-features = false }
 pallet-membership = { version = "2.0.0", default-features = false }
 pallet-multisig = { version = "2.0.0", default-features = false }
 pallet-offences = { version = "2.0.0", default-features = false }
+pallet-proxy = { version = "2.0.0", default-features = false }
 pallet-randomness-collective-flip = { version = "2.0.0", default-features = false }
 pallet-scheduler = { version = "2.0.0", default-features = false }
 pallet-session = { version = "2.0.0", default-features = false, features = ["historical"] }
@@ -136,6 +137,7 @@ std = [
     "pallet-membership/std",
     "pallet-multisig/std",
     "pallet-offences/std",
+    "pallet-proxy/std",
     "pallet-randomness-collective-flip/std",
     "pallet-scheduler/std",
     "pallet-session/std",
diff --git a/runtime/malan/src/lib.rs b/runtime/malan/src/lib.rs
index 234bf1189..5a6c169eb 100644
--- a/runtime/malan/src/lib.rs
+++ b/runtime/malan/src/lib.rs
@@ -10,7 +10,7 @@
 #[cfg(feature = "std")]
 include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
 
-use codec::Encode;
+use codec::{Decode, Encode};
 use static_assertions::const_assert;
 
 use sp_api::impl_runtime_apis;
@@ -31,7 +31,7 @@ use sp_runtime::{
         InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity,
         TransactionValidityError, ValidTransaction,
     },
-    ApplyExtrinsicResult, DispatchError, ModuleId, Perbill, Percent, Permill,
+    ApplyExtrinsicResult, DispatchError, ModuleId, Perbill, Percent, Permill, RuntimeDebug,
 };
 use sp_std::{collections::btree_map::BTreeMap, prelude::*};
 #[cfg(feature = "std")]
@@ -104,7 +104,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
     spec_name: create_runtime_str!("chainx"),
     impl_name: create_runtime_str!("chainx-malan"),
     authoring_version: 1,
-    spec_version: 1,
+    spec_version: 2,
     impl_version: 1,
     apis: RUNTIME_API_VERSIONS,
     transaction_version: 1,
@@ -758,6 +758,115 @@ impl pallet_identity::Trait for Runtime {
     type WeightInfo = pallet_identity::weights::SubstrateWeight<Runtime>;
 }
 
+parameter_types! {
+    // One storage item; key size 32, value size 8; .
+    pub const ProxyDepositBase: Balance = deposit(1, 8);
+    // Additional storage item size of 33 bytes.
+    pub const ProxyDepositFactor: Balance = deposit(0, 33);
+    pub const MaxProxies: u16 = 32;
+    pub const AnnouncementDepositBase: Balance = deposit(1, 8);
+    pub const AnnouncementDepositFactor: Balance = deposit(0, 66);
+    pub const MaxPending: u16 = 32;
+}
+
+/// The type used to represent the kinds of proxying allowed.
+#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, RuntimeDebug)]
+pub enum ProxyType {
+    Any = 0,
+    NonTransfer = 1,
+    Governance = 2,
+    Staking = 3,
+    IdentityJudgement = 4,
+    CancelProxy = 5,
+}
+
+impl Default for ProxyType {
+    fn default() -> Self {
+        Self::Any
+    }
+}
+
+impl InstanceFilter<Call> for ProxyType {
+    fn filter(&self, c: &Call) -> bool {
+        match self {
+            ProxyType::Any => true,
+            ProxyType::NonTransfer => matches!(
+                c,
+                Call::System(..)
+                    | Call::Scheduler(..)
+                    | Call::Babe(..)
+                    | Call::Timestamp(..)
+                    | Call::Indices(pallet_indices::Call::claim(..))
+                    | Call::Indices(pallet_indices::Call::free(..))
+                    | Call::Indices(pallet_indices::Call::freeze(..))
+                    // Specifically omitting Indices `transfer`, `force_transfer`
+                    // Specifically omitting the entire Balances pallet
+                    | Call::Authorship(..)
+                    | Call::XStaking(..)
+                    | Call::Offences(..)
+                    | Call::Session(..)
+                    | Call::Grandpa(..)
+                    | Call::ImOnline(..)
+                    | Call::AuthorityDiscovery(..)
+                    | Call::Democracy(..)
+                    | Call::Council(..)
+                    | Call::TechnicalCommittee(..)
+                    | Call::Elections(..)
+                    | Call::TechnicalMembership(..)
+                    | Call::Treasury(..)
+                    | Call::Utility(..)
+                    | Call::Identity(..)
+                    | Call::Proxy(..)
+                    | Call::Multisig(..)
+            ),
+            ProxyType::Governance => matches!(
+                c,
+                Call::Democracy(..)
+                    | Call::Council(..)
+                    | Call::TechnicalCommittee(..)
+                    | Call::Elections(..)
+                    | Call::Treasury(..)
+                    | Call::Utility(..)
+            ),
+            ProxyType::Staking => matches!(
+                c,
+                Call::XStaking(..) | Call::Session(..) | Call::Utility(..)
+            ),
+            ProxyType::IdentityJudgement => matches!(
+                c,
+                Call::Identity(pallet_identity::Call::provide_judgement(..)) | Call::Utility(..)
+            ),
+            ProxyType::CancelProxy => {
+                matches!(c, Call::Proxy(pallet_proxy::Call::reject_announcement(..)))
+            }
+        }
+    }
+    fn is_superset(&self, o: &Self) -> bool {
+        match (self, o) {
+            (x, y) if x == y => true,
+            (ProxyType::Any, _) => true,
+            (_, ProxyType::Any) => false,
+            (ProxyType::NonTransfer, _) => true,
+            _ => false,
+        }
+    }
+}
+
+impl pallet_proxy::Trait for Runtime {
+    type Event = Event;
+    type Call = Call;
+    type Currency = Balances;
+    type ProxyType = ProxyType;
+    type ProxyDepositBase = ProxyDepositBase;
+    type ProxyDepositFactor = ProxyDepositFactor;
+    type MaxProxies = MaxProxies;
+    type WeightInfo = pallet_proxy::weights::SubstrateWeight<Runtime>;
+    type MaxPending = MaxPending;
+    type CallHasher = BlakeTwo256;
+    type AnnouncementDepositBase = AnnouncementDepositBase;
+    type AnnouncementDepositFactor = AnnouncementDepositFactor;
+}
+
 impl pallet_sudo::Trait for Runtime {
     type Event = Event;
     type Call = Call;
@@ -962,7 +1071,9 @@ construct_runtime!(
         // we put it at the end for keeping the extrinsic ordering.
         XTransactionFee: xpallet_transaction_fee::{Module, Event<T>} = 35,
 
-        Sudo: pallet_sudo::{Module, Call, Config<T>, Storage, Event<T>},
+        Sudo: pallet_sudo::{Module, Call, Config<T>, Storage, Event<T>} = 36,
+
+        Proxy: pallet_proxy::{Module, Call, Storage, Event<T>} = 37,
     }
 );
 


Test patch: