feat: Add pallet-proxy-genesis-companion from Moonbeam (#419)

## Summary

- Import `pallet-proxy-genesis-companion` from Moonbeam to enable proxy
account configuration at genesis time
- Configure the pallet in all runtimes (mainnet, stagenet, testnet) with
pallet index 106
- Add `Serialize`/`Deserialize` derives to `ProxyType` enum to satisfy
`MaybeSerializeDeserialize` bounds
- Include mock runtime and unit tests adapted for polkadot-stable2412-6

This pallet extends `pallet-proxy` with genesis configuration support,
allowing proxy relationships to be established at chain genesis rather
than requiring extrinsic calls after launch.

### Key adaptations from Moonbeam

The pallet was modified to work with the DataHaven SDK version
(polkadot-stable2412-6):
- Removed `BlockNumberProvider` associated type constraint (not present
in this version of pallet-proxy)
- Uses `frame_system::pallet_prelude::BlockNumberFor<T>` directly for
delay parameter
- Uses `MaybeSerializeDeserialize` trait bound for `ProxyType`

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
Co-authored-by: Ahmad Kaouk <ahmadkaouk.93@gmail.com>
This commit is contained in:
Steve Degosserie 2026-01-30 12:16:28 +01:00 committed by GitHub
parent 5f87493080
commit ce24450b70
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 427 additions and 1 deletions

23
operator/Cargo.lock generated
View file

@ -2684,6 +2684,7 @@ dependencies = [
"pallet-proofs-dealer",
"pallet-proofs-dealer-runtime-api",
"pallet-proxy",
"pallet-proxy-genesis-companion",
"pallet-randomness",
"pallet-referenda",
"pallet-safe-mode",
@ -2705,6 +2706,7 @@ dependencies = [
"polkadot-runtime-common",
"precompile-utils",
"scale-info",
"serde",
"serde_json",
"shc-common",
"shp-constants",
@ -2982,6 +2984,7 @@ dependencies = [
"pallet-proofs-dealer",
"pallet-proofs-dealer-runtime-api",
"pallet-proxy",
"pallet-proxy-genesis-companion",
"pallet-randomness",
"pallet-referenda",
"pallet-safe-mode",
@ -3003,6 +3006,7 @@ dependencies = [
"polkadot-runtime-common",
"precompile-utils",
"scale-info",
"serde",
"serde_json",
"shc-common",
"shp-constants",
@ -3135,6 +3139,7 @@ dependencies = [
"pallet-proofs-dealer",
"pallet-proofs-dealer-runtime-api",
"pallet-proxy",
"pallet-proxy-genesis-companion",
"pallet-randomness",
"pallet-referenda",
"pallet-safe-mode",
@ -3156,6 +3161,7 @@ dependencies = [
"polkadot-runtime-common",
"precompile-utils",
"scale-info",
"serde",
"serde_json",
"shc-common",
"shp-constants",
@ -9512,6 +9518,23 @@ dependencies = [
"scale-info",
]
[[package]]
name = "pallet-proxy-genesis-companion"
version = "0.21.0"
dependencies = [
"frame-support",
"frame-system",
"pallet-balances",
"pallet-proxy",
"parity-scale-codec",
"scale-info",
"serde",
"sp-core",
"sp-io",
"sp-runtime",
"sp-std",
]
[[package]]
name = "pallet-randomness"
version = "0.3.5"

View file

@ -43,6 +43,7 @@ pallet-external-validator-slashes = { path = "./pallets/external-validator-slash
pallet-external-validators = { path = "./pallets/external-validators", default-features = false }
pallet-external-validators-rewards = { path = "./pallets/external-validators-rewards", default-features = false }
pallet-outbound-commitment-store = { path = "./pallets/outbound-commitment-store", default-features = false }
pallet-proxy-genesis-companion = { path = "./pallets/proxy-genesis-companion", default-features = false }
# Crates.io (wasm)
alloy-core = { version = "0.8.15", default-features = false }

View file

@ -0,0 +1,40 @@
[package]
name = "pallet-proxy-genesis-companion"
authors = { workspace = true }
description = "A simple pallet that expands pallet-proxy with a genesis configuration"
edition = { workspace = true }
version = { workspace = true }
license = { workspace = true }
[dependencies]
codec = { workspace = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
pallet-proxy = { workspace = true }
scale-info = { workspace = true, features = ["derive"] }
sp-runtime = { workspace = true }
sp-std = { workspace = true }
[dev-dependencies]
pallet-balances = { workspace = true, features = ["insecure_zero_ed", "std"] }
serde = { workspace = true, features = ["derive", "std"] }
sp-core = { workspace = true, features = ["std"] }
sp-io = { workspace = true, features = ["std"] }
[features]
default = ["std"]
std = [
"codec/std",
"frame-support/std",
"frame-system/std",
"pallet-proxy/std",
"scale-info/std",
"sp-runtime/std",
"sp-std/std",
]
try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
"pallet-proxy/try-runtime",
"sp-runtime/try-runtime",
]

View file

@ -0,0 +1,85 @@
// Copyright 2019-2025 PureStake Inc.
// This file is part of Moonbeam.
// Moonbeam is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Moonbeam is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Moonbeam. If not, see <http://www.gnu.org/licenses/>.
//! A companion pallet to pallet-proxy
//!
//! This pallet allows you to specify proxy accounts that will exist from genesis. This
//! functionality could be moved upstream into pallet proxy eventually, but for now there are fewer
//! obstacles to including it here.
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
use frame_support::pallet;
pub use pallet::*;
#[pallet]
pub mod pallet {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::BlockNumberFor;
use sp_std::vec::Vec;
/// Pallet for configuring proxy at genesis
#[pallet::pallet]
#[pallet::without_storage_info]
pub struct Pallet<T>(PhantomData<T>);
/// This pallet requires pallet-proxy to be installed.
#[pallet::config]
pub trait Config:
frame_system::Config + pallet_proxy::Config<ProxyType = <Self as Config>::ProxyType>
{
/// This MUST be the same as in pallet_proxy or it won't compile
type ProxyType: MaybeSerializeDeserialize + Clone;
}
#[pallet::genesis_config]
pub struct GenesisConfig<T: Config> {
pub proxies: Vec<(
T::AccountId,
T::AccountId,
<T as Config>::ProxyType,
BlockNumberFor<T>,
)>,
}
impl<T: Config> Default for GenesisConfig<T> {
fn default() -> Self {
Self {
proxies: Vec::new(),
}
}
}
#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {
for (delegator, delegatee, proxy_type, delay) in &self.proxies {
pallet_proxy::Pallet::<T>::add_proxy_delegate(
delegator,
delegatee.clone(),
proxy_type.clone(),
*delay,
)
.expect("Genesis proxy could not be added");
}
}
}
}

View file

@ -0,0 +1,172 @@
// Copyright 2019-2025 PureStake Inc.
// This file is part of Moonbeam.
// Moonbeam is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Moonbeam is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Moonbeam. If not, see <http://www.gnu.org/licenses/>.
//! A minimal runtime including the proxy-genesis-companion pallet
use super::*;
use crate as proxy_companion;
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{
construct_runtime, derive_impl, parameter_types,
traits::{ConstU32, InstanceFilter},
};
use sp_runtime::{traits::BlakeTwo256, BuildStorage};
pub type AccountId = u64;
pub type Balance = u128;
type Block = frame_system::mocking::MockBlock<Test>;
// Configure a mock runtime to test the pallet.
construct_runtime!(
pub enum Test
{
System: frame_system,
Balances: pallet_balances,
Proxy: pallet_proxy,
ProxyGenesisCompanion: proxy_companion,
}
);
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Test {
type Block = Block;
type AccountData = pallet_balances::AccountData<Balance>;
}
parameter_types! {
pub const ExistentialDeposit: u128 = 0;
}
#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
impl pallet_balances::Config for Test {
type Balance = Balance;
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = System;
}
parameter_types! {
pub const ProxyDepositBase: Balance = 1;
pub const ProxyDepositFactor: Balance = 1;
pub const MaxProxies: u16 = 32;
pub const AnnouncementDepositBase: Balance = 1;
pub const AnnouncementDepositFactor: Balance = 1;
pub const MaxPending: u16 = 32;
}
/// The type used to represent the kinds of proxying allowed.
#[derive(
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
Encode,
Decode,
Debug,
MaxEncodedLen,
scale_info::TypeInfo,
serde::Serialize,
serde::Deserialize,
Default,
)]
pub struct ProxyType;
impl InstanceFilter<RuntimeCall> for ProxyType {
fn filter(&self, _c: &RuntimeCall) -> bool {
true
}
fn is_superset(&self, _o: &Self) -> bool {
true
}
}
impl pallet_proxy::Config for Test {
type RuntimeEvent = RuntimeEvent;
type RuntimeCall = RuntimeCall;
type Currency = Balances;
type ProxyType = ProxyType;
type ProxyDepositBase = ProxyDepositBase;
type ProxyDepositFactor = ProxyDepositFactor;
type MaxProxies = ConstU32<32>;
type WeightInfo = ();
type MaxPending = ConstU32<32>;
type CallHasher = BlakeTwo256;
type AnnouncementDepositBase = AnnouncementDepositBase;
type AnnouncementDepositFactor = AnnouncementDepositFactor;
}
impl Config for Test {
type ProxyType = ProxyType;
}
/// Externality builder for pallet proxy genesis companion's mock runtime
pub(crate) struct ExtBuilder {
proxies: Vec<(AccountId, AccountId)>,
balances: Vec<(AccountId, Balance)>,
}
impl Default for ExtBuilder {
fn default() -> ExtBuilder {
ExtBuilder {
proxies: Vec::new(),
balances: Vec::new(),
}
}
}
impl ExtBuilder {
pub(crate) fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self {
self.balances = balances;
self
}
pub(crate) fn with_proxies(mut self, proxies: Vec<(AccountId, AccountId)>) -> Self {
self.proxies = proxies;
self
}
pub(crate) fn build(self) -> sp_io::TestExternalities {
let mut t = frame_system::GenesisConfig::<Test>::default()
.build_storage()
.expect("Frame system builds valid default genesis config");
pallet_balances::GenesisConfig::<Test> {
balances: self.balances,
..Default::default()
}
.assimilate_storage(&mut t)
.expect("Pallet balances storage can be assimilated");
let genesis_config = proxy_companion::GenesisConfig::<Test> {
// Here we add the trivial proxy type and default duration.
// This saves the test writer from having to always specify this.
proxies: self
.proxies
.into_iter()
.map(|(a, b)| (a, b, ProxyType, 100))
.collect(),
};
genesis_config
.assimilate_storage(&mut t)
.expect("Pallet proxy genesis companion storage can be assimilated");
let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| System::set_block_number(1));
ext
}
}

View file

@ -0,0 +1,63 @@
// Copyright 2019-2025 PureStake Inc.
// This file is part of Moonbeam.
// Moonbeam is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Moonbeam is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Moonbeam. If not, see <http://www.gnu.org/licenses/>.
//! Unit testing
use crate::mock::{ExtBuilder, Proxy, ProxyType, Test};
use pallet_proxy::ProxyDefinition;
#[test]
fn empty_genesis_works() {
ExtBuilder::default()
.build()
.execute_with(|| assert_eq!(pallet_proxy::Proxies::<Test>::iter().count(), 0))
}
#[test]
fn non_empty_genesis_works() {
ExtBuilder::default()
// Account 1 delegates to account 2
.with_proxies(vec![(1, 2)])
// Account 1 is funded to pay the proxy deposit
.with_balances(vec![(1, 10)])
.build()
.execute_with(|| {
// Lookup info that we expect to be stored from genesis
let (proxy_defs, deposit) = Proxy::proxies(1);
// Make sure that Account 1 delegates to Account 2 and nobody else
assert_eq!(proxy_defs.len(), 1);
assert_eq!(
proxy_defs[0],
ProxyDefinition {
delegate: 2,
proxy_type: ProxyType,
delay: 100
}
);
// Make sure that Account 1 has the proper deposit amount reserved
assert_eq!(deposit, 2);
})
}
#[test]
#[should_panic(expected = "Genesis proxy could not be added")]
fn genesis_fails_if_balance_insufficient() {
ExtBuilder::default()
.with_proxies(vec![(1, 2)])
.build()
.execute_with(|| ())
}

View file

@ -67,6 +67,7 @@ pallet-outbound-commitment-store = { workspace = true }
pallet-parameters = { workspace = true }
pallet-preimage = { workspace = true }
pallet-proxy = { workspace = true }
pallet-proxy-genesis-companion = { workspace = true }
pallet-referenda = { workspace = true }
pallet-safe-mode = { workspace = true }
pallet-scheduler = { workspace = true }
@ -84,6 +85,7 @@ polkadot-primitives = { workspace = true }
polkadot-runtime-common = { workspace = true }
precompile-utils = { workspace = true }
scale-info = { workspace = true, features = ["derive", "serde"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, default-features = false, features = [
"alloc",
] }
@ -235,6 +237,7 @@ std = [
"pallet-tx-pause/std",
"pallet-referenda/std",
"pallet-proxy/std",
"pallet-proxy-genesis-companion/std",
"pallet-scheduler/std",
"pallet-session/std",
"pallet-sudo/std",
@ -248,6 +251,7 @@ std = [
"polkadot-runtime-common/std",
"precompile-utils/std",
"scale-info/std",
"serde/std",
"serde_json/std",
"snowbridge-beacon-primitives/std",
"snowbridge-inbound-queue-primitives/std",
@ -390,6 +394,7 @@ try-runtime = [
"pallet-tx-pause/try-runtime",
"pallet-referenda/try-runtime",
"pallet-proxy/try-runtime",
"pallet-proxy-genesis-companion/try-runtime",
"pallet-scheduler/try-runtime",
"pallet-session/try-runtime",
"pallet-sudo/try-runtime",

View file

@ -49,6 +49,8 @@ use sp_runtime::{traits::AccountIdConversion, RuntimeDebug};
RuntimeDebug,
MaxEncodedLen,
TypeInfo,
serde::Serialize,
serde::Deserialize,
)]
pub enum ProxyType {
/// Allow any call to be made by the proxy account
@ -829,6 +831,10 @@ impl pallet_proxy::Config for Runtime {
type AnnouncementDepositFactor = AnnouncementDepositFactor;
}
impl pallet_proxy_genesis_companion::Config for Runtime {
type ProxyType = ProxyType;
}
impl pallet_parameters::Config for Runtime {
type AdminOrigin = EnsureRoot<AccountId>;
type RuntimeEvent = RuntimeEvent;

View file

@ -500,6 +500,9 @@ mod runtime {
#[runtime::pallet_index(105)]
pub type ExternalValidatorsSlashes = pallet_external_validator_slashes;
#[runtime::pallet_index(106)]
pub type ProxyGenesisCompanion = pallet_proxy_genesis_companion;
// ╚═══════════════════ DataHaven-specific Pallets ══════════════════╝
}

View file

@ -67,6 +67,7 @@ pallet-outbound-commitment-store = { workspace = true }
pallet-parameters = { workspace = true }
pallet-preimage = { workspace = true }
pallet-proxy = { workspace = true }
pallet-proxy-genesis-companion = { workspace = true }
pallet-referenda = { workspace = true }
pallet-safe-mode = { workspace = true }
pallet-scheduler = { workspace = true }
@ -84,6 +85,7 @@ polkadot-primitives = { workspace = true }
polkadot-runtime-common = { workspace = true }
precompile-utils = { workspace = true }
scale-info = { workspace = true, features = ["derive", "serde"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, default-features = false, features = [
"alloc",
] }
@ -236,6 +238,7 @@ std = [
"pallet-tx-pause/std",
"pallet-referenda/std",
"pallet-proxy/std",
"pallet-proxy-genesis-companion/std",
"pallet-scheduler/std",
"pallet-session/std",
"pallet-sudo/std",
@ -249,6 +252,7 @@ std = [
"polkadot-runtime-common/std",
"precompile-utils/std",
"scale-info/std",
"serde/std",
"serde_json/std",
"snowbridge-beacon-primitives/std",
"snowbridge-inbound-queue-primitives/std",
@ -391,6 +395,7 @@ try-runtime = [
"pallet-tx-pause/try-runtime",
"pallet-referenda/try-runtime",
"pallet-proxy/try-runtime",
"pallet-proxy-genesis-companion/try-runtime",
"pallet-scheduler/try-runtime",
"pallet-session/try-runtime",
"pallet-sudo/try-runtime",

View file

@ -49,6 +49,8 @@ use sp_runtime::{traits::AccountIdConversion, RuntimeDebug};
RuntimeDebug,
MaxEncodedLen,
TypeInfo,
serde::Serialize,
serde::Deserialize,
)]
pub enum ProxyType {
/// Allow any call to be made by the proxy account
@ -826,6 +828,10 @@ impl pallet_proxy::Config for Runtime {
type AnnouncementDepositFactor = AnnouncementDepositFactor;
}
impl pallet_proxy_genesis_companion::Config for Runtime {
type ProxyType = ProxyType;
}
impl pallet_parameters::Config for Runtime {
type AdminOrigin = EnsureRoot<AccountId>;
type RuntimeEvent = RuntimeEvent;

View file

@ -502,6 +502,9 @@ mod runtime {
#[runtime::pallet_index(105)]
pub type ExternalValidatorsSlashes = pallet_external_validator_slashes;
#[runtime::pallet_index(106)]
pub type ProxyGenesisCompanion = pallet_proxy_genesis_companion;
// ╚═══════════════════ DataHaven-specific Pallets ══════════════════╝
}

View file

@ -68,6 +68,7 @@ pallet-outbound-commitment-store = { workspace = true }
pallet-parameters = { workspace = true }
pallet-preimage = { workspace = true }
pallet-proxy = { workspace = true }
pallet-proxy-genesis-companion = { workspace = true }
pallet-referenda = { workspace = true }
pallet-safe-mode = { workspace = true }
pallet-scheduler = { workspace = true }
@ -85,6 +86,7 @@ polkadot-primitives = { workspace = true }
polkadot-runtime-common = { workspace = true }
precompile-utils = { workspace = true }
scale-info = { workspace = true, features = ["derive", "serde"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, default-features = false, features = [
"alloc",
] }
@ -234,6 +236,7 @@ std = [
"pallet-tx-pause/std",
"pallet-referenda/std",
"pallet-proxy/std",
"pallet-proxy-genesis-companion/std",
"pallet-scheduler/std",
"pallet-session/std",
"pallet-sudo/std",
@ -246,6 +249,7 @@ std = [
"polkadot-primitives/std",
"polkadot-runtime-common/std",
"scale-info/std",
"serde/std",
"serde_json/std",
"snowbridge-beacon-primitives/std",
"snowbridge-inbound-queue-primitives/std",
@ -387,6 +391,7 @@ try-runtime = [
"pallet-tx-pause/try-runtime",
"pallet-referenda/try-runtime",
"pallet-proxy/try-runtime",
"pallet-proxy-genesis-companion/try-runtime",
"pallet-scheduler/try-runtime",
"pallet-session/try-runtime",
"pallet-sudo/try-runtime",

View file

@ -49,6 +49,8 @@ use sp_runtime::{traits::AccountIdConversion, RuntimeDebug};
RuntimeDebug,
MaxEncodedLen,
TypeInfo,
serde::Serialize,
serde::Deserialize,
)]
pub enum ProxyType {
/// Allow any call to be made by the proxy account
@ -829,6 +831,10 @@ impl pallet_proxy::Config for Runtime {
type AnnouncementDepositFactor = AnnouncementDepositFactor;
}
impl pallet_proxy_genesis_companion::Config for Runtime {
type ProxyType = ProxyType;
}
impl pallet_parameters::Config for Runtime {
type AdminOrigin = EnsureRoot<AccountId>;
type RuntimeEvent = RuntimeEvent;

View file

@ -500,6 +500,9 @@ mod runtime {
#[runtime::pallet_index(105)]
pub type ExternalValidatorsSlashes = pallet_external_validator_slashes;
#[runtime::pallet_index(106)]
pub type ProxyGenesisCompanion = pallet_proxy_genesis_companion;
// ╚═══════════════════ DataHaven-specific Pallets ══════════════════╝
}

View file

@ -1,5 +1,5 @@
{
"version": "0.1.0-autogenerated.18110841939960448741",
"version": "0.1.0-autogenerated.14314240478086326730",
"name": "@polkadot-api/descriptors",
"files": [
"dist"

Binary file not shown.