From c078d2f41cf8ecd28ff5279fcebb22da418a14b9 Mon Sep 17 00:00:00 2001
From: Daniel Olano <daniel@olanod.com>
Date: Mon, 17 Feb 2025 13:18:01 +0100
Subject: [PATCH] Change pallet referenda TracksInfo::tracks to return an
 iterator (#2072)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Returning an iterator in `TracksInfo::tracks()` instead of a static
slice allows for more flexible implementations of `TracksInfo` that can
use the chain storage without compromising a lot on the
performance/memory penalty if we were to return an owned `Vec` instead.

---------

Co-authored-by: Pablo Andrés Dorado Suárez <hola@pablodorado.com>
---
 .../src/ambassador/tracks.rs                  | 106 ++++-----
 .../src/fellowship/tracks.rs                  | 223 +++++++++---------
 .../rococo/src/governance/fellowship.rs       | 115 ++++-----
 .../runtime/rococo/src/governance/tracks.rs   | 163 ++++++-------
 .../runtime/westend/src/governance/tracks.rs  | 163 ++++++-------
 prdoc/pr_2072.prdoc                           |  23 ++
 substrate/bin/node/runtime/src/lib.rs         |  90 ++++---
 substrate/frame/referenda/src/benchmarking.rs |   4 +-
 substrate/frame/referenda/src/lib.rs          |  81 +++----
 substrate/frame/referenda/src/mock.rs         |  42 ++--
 substrate/frame/referenda/src/tests.rs        |   5 +-
 substrate/frame/referenda/src/types.rs        | 101 +++++---
 12 files changed, 592 insertions(+), 524 deletions(-)
 create mode 100644 prdoc/pr_2072.prdoc

diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/ambassador/tracks.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/ambassador/tracks.rs
index d4a2d3bbf1c..aa1f67d18af 100644
--- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/ambassador/tracks.rs
+++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/ambassador/tracks.rs
@@ -17,7 +17,8 @@
 
 use super::Origin;
 use crate::{Balance, BlockNumber, RuntimeOrigin, DAYS, DOLLARS, HOURS};
-use sp_runtime::Perbill;
+use sp_runtime::{str_array as s, Perbill};
+use sp_std::borrow::Cow;
 
 /// Referendum `TrackId` type.
 pub type TrackId = u16;
@@ -46,13 +47,15 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 
 	type RuntimeOrigin = <RuntimeOrigin as frame_support::traits::OriginTrait>::PalletsOrigin;
 
-	/// Return the array of available tracks and their information.
-	fn tracks() -> &'static [(Self::Id, pallet_referenda::TrackInfo<Balance, BlockNumber>)] {
-		static DATA: [(TrackId, pallet_referenda::TrackInfo<Balance, BlockNumber>); 9] = [
-			(
-				constants::AMBASSADOR_TIER_1,
-				pallet_referenda::TrackInfo {
-					name: "ambassador tier 1",
+	/// Return the list of available tracks and their information.
+	fn tracks(
+	) -> impl Iterator<Item = Cow<'static, pallet_referenda::Track<Self::Id, Balance, BlockNumber>>>
+	{
+		static DATA: [pallet_referenda::Track<TrackId, Balance, BlockNumber>; 9] = [
+			pallet_referenda::Track {
+				id: constants::AMBASSADOR_TIER_1,
+				info: pallet_referenda::TrackInfo {
+					name: s("ambassador tier 1"),
 					max_deciding: 10,
 					decision_deposit: 5 * DOLLARS,
 					prepare_period: 24 * HOURS,
@@ -70,11 +73,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(50),
 					},
 				},
-			),
-			(
-				constants::AMBASSADOR_TIER_2,
-				pallet_referenda::TrackInfo {
-					name: "ambassador tier 2",
+			},
+			pallet_referenda::Track {
+				id: constants::AMBASSADOR_TIER_2,
+				info: pallet_referenda::TrackInfo {
+					name: s("ambassador tier 2"),
 					max_deciding: 10,
 					decision_deposit: 5 * DOLLARS,
 					prepare_period: 24 * HOURS,
@@ -92,11 +95,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(50),
 					},
 				},
-			),
-			(
-				constants::SENIOR_AMBASSADOR_TIER_3,
-				pallet_referenda::TrackInfo {
-					name: "senior ambassador tier 3",
+			},
+			pallet_referenda::Track {
+				id: constants::SENIOR_AMBASSADOR_TIER_3,
+				info: pallet_referenda::TrackInfo {
+					name: s("senior ambassador tier 3"),
 					max_deciding: 10,
 					decision_deposit: 5 * DOLLARS,
 					prepare_period: 24 * HOURS,
@@ -114,11 +117,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(50),
 					},
 				},
-			),
-			(
-				constants::SENIOR_AMBASSADOR_TIER_4,
-				pallet_referenda::TrackInfo {
-					name: "senior ambassador tier 4",
+			},
+			pallet_referenda::Track {
+				id: constants::SENIOR_AMBASSADOR_TIER_4,
+				info: pallet_referenda::TrackInfo {
+					name: s("senior ambassador tier 4"),
 					max_deciding: 10,
 					decision_deposit: 5 * DOLLARS,
 					prepare_period: 24 * HOURS,
@@ -136,11 +139,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(50),
 					},
 				},
-			),
-			(
-				constants::HEAD_AMBASSADOR_TIER_5,
-				pallet_referenda::TrackInfo {
-					name: "head ambassador tier 5",
+			},
+			pallet_referenda::Track {
+				id: constants::HEAD_AMBASSADOR_TIER_5,
+				info: pallet_referenda::TrackInfo {
+					name: s("head ambassador tier 5"),
 					max_deciding: 10,
 					decision_deposit: 5 * DOLLARS,
 					prepare_period: 24 * HOURS,
@@ -158,11 +161,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(50),
 					},
 				},
-			),
-			(
-				constants::HEAD_AMBASSADOR_TIER_6,
-				pallet_referenda::TrackInfo {
-					name: "head ambassador tier 6",
+			},
+			pallet_referenda::Track {
+				id: constants::HEAD_AMBASSADOR_TIER_6,
+				info: pallet_referenda::TrackInfo {
+					name: s("head ambassador tier 6"),
 					max_deciding: 10,
 					decision_deposit: 5 * DOLLARS,
 					prepare_period: 24 * HOURS,
@@ -180,11 +183,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(50),
 					},
 				},
-			),
-			(
-				constants::HEAD_AMBASSADOR_TIER_7,
-				pallet_referenda::TrackInfo {
-					name: "head ambassador tier 7",
+			},
+			pallet_referenda::Track {
+				id: constants::HEAD_AMBASSADOR_TIER_7,
+				info: pallet_referenda::TrackInfo {
+					name: s("head ambassador tier 7"),
 					max_deciding: 10,
 					decision_deposit: 5 * DOLLARS,
 					prepare_period: 24 * HOURS,
@@ -202,11 +205,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(50),
 					},
 				},
-			),
-			(
-				constants::MASTER_AMBASSADOR_TIER_8,
-				pallet_referenda::TrackInfo {
-					name: "master ambassador tier 8",
+			},
+			pallet_referenda::Track {
+				id: constants::MASTER_AMBASSADOR_TIER_8,
+				info: pallet_referenda::TrackInfo {
+					name: s("master ambassador tier 8"),
 					max_deciding: 10,
 					decision_deposit: 5 * DOLLARS,
 					prepare_period: 24 * HOURS,
@@ -224,11 +227,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(50),
 					},
 				},
-			),
-			(
-				constants::MASTER_AMBASSADOR_TIER_9,
-				pallet_referenda::TrackInfo {
-					name: "master ambassador tier 9",
+			},
+			pallet_referenda::Track {
+				id: constants::MASTER_AMBASSADOR_TIER_9,
+				info: pallet_referenda::TrackInfo {
+					name: s("master ambassador tier 9"),
 					max_deciding: 10,
 					decision_deposit: 5 * DOLLARS,
 					prepare_period: 24 * HOURS,
@@ -246,9 +249,9 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(50),
 					},
 				},
-			),
+			},
 		];
-		&DATA[..]
+		DATA.iter().map(Cow::Borrowed)
 	}
 
 	/// Determine the voting track for the given `origin`.
@@ -277,6 +280,3 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 		}
 	}
 }
-
-// implements [`frame_support::traits::Get`] for [`TracksInfo`]
-pallet_referenda::impl_tracksinfo_get!(TracksInfo, Balance, BlockNumber);
diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/fellowship/tracks.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/fellowship/tracks.rs
index d7959f77244..6c3c88935dd 100644
--- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/fellowship/tracks.rs
+++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/fellowship/tracks.rs
@@ -18,7 +18,8 @@
 
 use crate::{Balance, BlockNumber, RuntimeOrigin, DAYS, DOLLARS, HOURS, MINUTES};
 use pallet_ranked_collective::Rank;
-use sp_runtime::{traits::Convert, Perbill};
+use sp_runtime::{str_array as s, traits::Convert, Perbill};
+use sp_std::borrow::Cow;
 
 /// Referendum `TrackId` type.
 pub type TrackId = u16;
@@ -114,13 +115,16 @@ pub struct TracksInfo;
 impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 	type Id = TrackId;
 	type RuntimeOrigin = <RuntimeOrigin as frame_support::traits::OriginTrait>::PalletsOrigin;
-	fn tracks() -> &'static [(Self::Id, pallet_referenda::TrackInfo<Balance, BlockNumber>)] {
+
+	fn tracks(
+	) -> impl Iterator<Item = Cow<'static, pallet_referenda::Track<Self::Id, Balance, BlockNumber>>>
+	{
 		use constants as tracks;
-		static DATA: [(TrackId, pallet_referenda::TrackInfo<Balance, BlockNumber>); 21] = [
-			(
-				tracks::MEMBERS,
-				pallet_referenda::TrackInfo {
-					name: "members",
+		static DATA: [pallet_referenda::Track<TrackId, Balance, BlockNumber>; 21] = [
+			pallet_referenda::Track {
+				id: tracks::MEMBERS,
+				info: pallet_referenda::TrackInfo {
+					name: s("members"),
 					max_deciding: 10,
 					decision_deposit: 5 * DOLLARS,
 					prepare_period: 30 * MINUTES,
@@ -138,11 +142,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(100),
 					},
 				},
-			),
-			(
-				tracks::PROFICIENTS,
-				pallet_referenda::TrackInfo {
-					name: "proficient members",
+			},
+			pallet_referenda::Track {
+				id: tracks::PROFICIENTS,
+				info: pallet_referenda::TrackInfo {
+					name: s("proficient members"),
 					max_deciding: 10,
 					decision_deposit: 5 * DOLLARS,
 					prepare_period: 30 * MINUTES,
@@ -160,11 +164,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(100),
 					},
 				},
-			),
-			(
-				tracks::FELLOWS,
-				pallet_referenda::TrackInfo {
-					name: "fellows",
+			},
+			pallet_referenda::Track {
+				id: tracks::FELLOWS,
+				info: pallet_referenda::TrackInfo {
+					name: s("fellows"),
 					max_deciding: 10,
 					decision_deposit: 5 * DOLLARS,
 					prepare_period: 30 * MINUTES,
@@ -182,11 +186,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(100),
 					},
 				},
-			),
-			(
-				tracks::ARCHITECTS,
-				pallet_referenda::TrackInfo {
-					name: "architects",
+			},
+			pallet_referenda::Track {
+				id: tracks::ARCHITECTS,
+				info: pallet_referenda::TrackInfo {
+					name: s("architects"),
 					max_deciding: 10,
 					decision_deposit: 5 * DOLLARS,
 					prepare_period: 30 * MINUTES,
@@ -204,11 +208,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(100),
 					},
 				},
-			),
-			(
-				tracks::ARCHITECTS_ADEPT,
-				pallet_referenda::TrackInfo {
-					name: "architects adept",
+			},
+			pallet_referenda::Track {
+				id: tracks::ARCHITECTS_ADEPT,
+				info: pallet_referenda::TrackInfo {
+					name: s("architects adept"),
 					max_deciding: 10,
 					decision_deposit: 5 * DOLLARS,
 					prepare_period: 30 * MINUTES,
@@ -226,11 +230,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(100),
 					},
 				},
-			),
-			(
-				tracks::GRAND_ARCHITECTS,
-				pallet_referenda::TrackInfo {
-					name: "grand architects",
+			},
+			pallet_referenda::Track {
+				id: tracks::GRAND_ARCHITECTS,
+				info: pallet_referenda::TrackInfo {
+					name: s("grand architects"),
 					max_deciding: 10,
 					decision_deposit: 5 * DOLLARS,
 					prepare_period: 30 * MINUTES,
@@ -248,11 +252,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(100),
 					},
 				},
-			),
-			(
-				tracks::MASTERS,
-				pallet_referenda::TrackInfo {
-					name: "masters",
+			},
+			pallet_referenda::Track {
+				id: tracks::MASTERS,
+				info: pallet_referenda::TrackInfo {
+					name: s("masters"),
 					max_deciding: 10,
 					decision_deposit: 5 * DOLLARS,
 					prepare_period: 30 * MINUTES,
@@ -270,11 +274,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(100),
 					},
 				},
-			),
-			(
-				tracks::MASTERS_CONSTANT,
-				pallet_referenda::TrackInfo {
-					name: "masters constant",
+			},
+			pallet_referenda::Track {
+				id: tracks::MASTERS_CONSTANT,
+				info: pallet_referenda::TrackInfo {
+					name: s("masters constant"),
 					max_deciding: 10,
 					decision_deposit: 5 * DOLLARS,
 					prepare_period: 30 * MINUTES,
@@ -292,11 +296,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(100),
 					},
 				},
-			),
-			(
-				tracks::GRAND_MASTERS,
-				pallet_referenda::TrackInfo {
-					name: "grand masters",
+			},
+			pallet_referenda::Track {
+				id: tracks::GRAND_MASTERS,
+				info: pallet_referenda::TrackInfo {
+					name: s("grand masters"),
 					max_deciding: 10,
 					decision_deposit: 5 * DOLLARS,
 					prepare_period: 30 * MINUTES,
@@ -314,11 +318,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(100),
 					},
 				},
-			),
-			(
-				tracks::RETAIN_AT_1DAN,
-				pallet_referenda::TrackInfo {
-					name: "retain at I Dan",
+			},
+			pallet_referenda::Track {
+				id: tracks::RETAIN_AT_1DAN,
+				info: pallet_referenda::TrackInfo {
+					name: s("retain at I Dan"),
 					max_deciding: RETAIN_MAX_DECIDING,
 					decision_deposit: RETAIN_DECISION_DEPOSIT,
 					prepare_period: RETAIN_PREPARE_PERIOD,
@@ -328,11 +332,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 					min_approval: RETAIN_MIN_APPROVAL,
 					min_support: RETAIN_MIN_SUPPORT,
 				},
-			),
-			(
-				tracks::RETAIN_AT_2DAN,
-				pallet_referenda::TrackInfo {
-					name: "retain at II Dan",
+			},
+			pallet_referenda::Track {
+				id: tracks::RETAIN_AT_2DAN,
+				info: pallet_referenda::TrackInfo {
+					name: s("retain at II Dan"),
 					max_deciding: RETAIN_MAX_DECIDING,
 					decision_deposit: RETAIN_DECISION_DEPOSIT,
 					prepare_period: RETAIN_PREPARE_PERIOD,
@@ -342,11 +346,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 					min_approval: RETAIN_MIN_APPROVAL,
 					min_support: RETAIN_MIN_SUPPORT,
 				},
-			),
-			(
-				tracks::RETAIN_AT_3DAN,
-				pallet_referenda::TrackInfo {
-					name: "retain at III Dan",
+			},
+			pallet_referenda::Track {
+				id: tracks::RETAIN_AT_3DAN,
+				info: pallet_referenda::TrackInfo {
+					name: s("retain at III Dan"),
 					max_deciding: RETAIN_MAX_DECIDING,
 					decision_deposit: RETAIN_DECISION_DEPOSIT,
 					prepare_period: RETAIN_PREPARE_PERIOD,
@@ -356,11 +360,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 					min_approval: RETAIN_MIN_APPROVAL,
 					min_support: RETAIN_MIN_SUPPORT,
 				},
-			),
-			(
-				tracks::RETAIN_AT_4DAN,
-				pallet_referenda::TrackInfo {
-					name: "retain at IV Dan",
+			},
+			pallet_referenda::Track {
+				id: tracks::RETAIN_AT_4DAN,
+				info: pallet_referenda::TrackInfo {
+					name: s("retain at IV Dan"),
 					max_deciding: RETAIN_MAX_DECIDING,
 					decision_deposit: RETAIN_DECISION_DEPOSIT,
 					prepare_period: RETAIN_PREPARE_PERIOD,
@@ -370,11 +374,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 					min_approval: RETAIN_MIN_APPROVAL,
 					min_support: RETAIN_MIN_SUPPORT,
 				},
-			),
-			(
-				tracks::RETAIN_AT_5DAN,
-				pallet_referenda::TrackInfo {
-					name: "retain at V Dan",
+			},
+			pallet_referenda::Track {
+				id: tracks::RETAIN_AT_5DAN,
+				info: pallet_referenda::TrackInfo {
+					name: s("retain at V Dan"),
 					max_deciding: RETAIN_MAX_DECIDING,
 					decision_deposit: RETAIN_DECISION_DEPOSIT,
 					prepare_period: RETAIN_PREPARE_PERIOD,
@@ -384,11 +388,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 					min_approval: RETAIN_MIN_APPROVAL,
 					min_support: RETAIN_MIN_SUPPORT,
 				},
-			),
-			(
-				tracks::RETAIN_AT_6DAN,
-				pallet_referenda::TrackInfo {
-					name: "retain at VI Dan",
+			},
+			pallet_referenda::Track {
+				id: tracks::RETAIN_AT_6DAN,
+				info: pallet_referenda::TrackInfo {
+					name: s("retain at VI Dan"),
 					max_deciding: RETAIN_MAX_DECIDING,
 					decision_deposit: RETAIN_DECISION_DEPOSIT,
 					prepare_period: RETAIN_PREPARE_PERIOD,
@@ -398,11 +402,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 					min_approval: RETAIN_MIN_APPROVAL,
 					min_support: RETAIN_MIN_SUPPORT,
 				},
-			),
-			(
-				tracks::PROMOTE_TO_1DAN,
-				pallet_referenda::TrackInfo {
-					name: "promote to I Dan",
+			},
+			pallet_referenda::Track {
+				id: tracks::PROMOTE_TO_1DAN,
+				info: pallet_referenda::TrackInfo {
+					name: s("promote to I Dan"),
 					max_deciding: PROMOTE_MAX_DECIDING,
 					decision_deposit: PROMOTE_DECISION_DEPOSIT,
 					prepare_period: PROMOTE_PREPARE_PERIOD,
@@ -412,11 +416,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 					min_approval: PROMOTE_MIN_APPROVAL,
 					min_support: PROMOTE_MIN_SUPPORT,
 				},
-			),
-			(
-				tracks::PROMOTE_TO_2DAN,
-				pallet_referenda::TrackInfo {
-					name: "promote to II Dan",
+			},
+			pallet_referenda::Track {
+				id: tracks::PROMOTE_TO_2DAN,
+				info: pallet_referenda::TrackInfo {
+					name: s("promote to II Dan"),
 					max_deciding: PROMOTE_MAX_DECIDING,
 					decision_deposit: PROMOTE_DECISION_DEPOSIT,
 					prepare_period: PROMOTE_PREPARE_PERIOD,
@@ -426,11 +430,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 					min_approval: PROMOTE_MIN_APPROVAL,
 					min_support: PROMOTE_MIN_SUPPORT,
 				},
-			),
-			(
-				tracks::PROMOTE_TO_3DAN,
-				pallet_referenda::TrackInfo {
-					name: "promote to III Dan",
+			},
+			pallet_referenda::Track {
+				id: tracks::PROMOTE_TO_3DAN,
+				info: pallet_referenda::TrackInfo {
+					name: s("promote to III Dan"),
 					max_deciding: PROMOTE_MAX_DECIDING,
 					decision_deposit: PROMOTE_DECISION_DEPOSIT,
 					prepare_period: PROMOTE_PREPARE_PERIOD,
@@ -440,11 +444,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 					min_approval: PROMOTE_MIN_APPROVAL,
 					min_support: PROMOTE_MIN_SUPPORT,
 				},
-			),
-			(
-				tracks::PROMOTE_TO_4DAN,
-				pallet_referenda::TrackInfo {
-					name: "promote to IV Dan",
+			},
+			pallet_referenda::Track {
+				id: tracks::PROMOTE_TO_4DAN,
+				info: pallet_referenda::TrackInfo {
+					name: s("promote to IV Dan"),
 					max_deciding: PROMOTE_MAX_DECIDING,
 					decision_deposit: PROMOTE_DECISION_DEPOSIT,
 					prepare_period: PROMOTE_PREPARE_PERIOD,
@@ -454,11 +458,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 					min_approval: PROMOTE_MIN_APPROVAL,
 					min_support: PROMOTE_MIN_SUPPORT,
 				},
-			),
-			(
-				tracks::PROMOTE_TO_5DAN,
-				pallet_referenda::TrackInfo {
-					name: "promote to V Dan",
+			},
+			pallet_referenda::Track {
+				id: tracks::PROMOTE_TO_5DAN,
+				info: pallet_referenda::TrackInfo {
+					name: s("promote to V Dan"),
 					max_deciding: PROMOTE_MAX_DECIDING,
 					decision_deposit: PROMOTE_DECISION_DEPOSIT,
 					prepare_period: PROMOTE_PREPARE_PERIOD,
@@ -468,11 +472,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 					min_approval: PROMOTE_MIN_APPROVAL,
 					min_support: PROMOTE_MIN_SUPPORT,
 				},
-			),
-			(
-				tracks::PROMOTE_TO_6DAN,
-				pallet_referenda::TrackInfo {
-					name: "promote to VI Dan",
+			},
+			pallet_referenda::Track {
+				id: tracks::PROMOTE_TO_6DAN,
+				info: pallet_referenda::TrackInfo {
+					name: s("promote to VI Dan"),
 					max_deciding: PROMOTE_MAX_DECIDING,
 					decision_deposit: PROMOTE_DECISION_DEPOSIT,
 					prepare_period: PROMOTE_PREPARE_PERIOD,
@@ -482,9 +486,9 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 					min_approval: PROMOTE_MIN_APPROVAL,
 					min_support: PROMOTE_MIN_SUPPORT,
 				},
-			),
+			},
 		];
-		&DATA[..]
+		DATA.iter().map(Cow::Borrowed)
 	}
 	fn track_for(id: &Self::RuntimeOrigin) -> Result<Self::Id, ()> {
 		use super::origins::Origin;
@@ -529,4 +533,3 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 		}
 	}
 }
-pallet_referenda::impl_tracksinfo_get!(TracksInfo, Balance, BlockNumber);
diff --git a/polkadot/runtime/rococo/src/governance/fellowship.rs b/polkadot/runtime/rococo/src/governance/fellowship.rs
index 231defab6aa..cb194e05e9c 100644
--- a/polkadot/runtime/rococo/src/governance/fellowship.rs
+++ b/polkadot/runtime/rococo/src/governance/fellowship.rs
@@ -16,8 +16,13 @@
 
 //! Elements of governance concerning the Rococo Fellowship.
 
+use alloc::borrow::Cow;
 use frame_support::traits::{MapSuccess, TryMapSuccess};
-use sp_runtime::traits::{CheckedReduceBy, ConstU16, Replace, ReplaceWithDefault};
+use pallet_referenda::{Track, TrackInfo};
+use sp_runtime::{
+	str_array as s,
+	traits::{CheckedReduceBy, ConstU16, Replace, ReplaceWithDefault},
+};
 
 use super::*;
 use crate::{CENTS, DAYS};
@@ -32,12 +37,13 @@ pub struct TracksInfo;
 impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 	type Id = u16;
 	type RuntimeOrigin = <RuntimeOrigin as frame_support::traits::OriginTrait>::PalletsOrigin;
-	fn tracks() -> &'static [(Self::Id, pallet_referenda::TrackInfo<Balance, BlockNumber>)] {
-		static DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 10] = [
-			(
-				0u16,
-				pallet_referenda::TrackInfo {
-					name: "candidates",
+
+	fn tracks() -> impl Iterator<Item = Cow<'static, Track<Self::Id, Balance, BlockNumber>>> {
+		static DATA: [Track<u16, Balance, BlockNumber>; 10] = [
+			Track {
+				id: 0u16,
+				info: TrackInfo {
+					name: s("candidates"),
 					max_deciding: 10,
 					decision_deposit: 100 * 3 * CENTS,
 					prepare_period: 30 * MINUTES,
@@ -55,11 +61,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(50),
 					},
 				},
-			),
-			(
-				1u16,
-				pallet_referenda::TrackInfo {
-					name: "members",
+			},
+			Track {
+				id: 1u16,
+				info: TrackInfo {
+					name: s("members"),
 					max_deciding: 10,
 					decision_deposit: 10 * 3 * CENTS,
 					prepare_period: 30 * MINUTES,
@@ -77,11 +83,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(50),
 					},
 				},
-			),
-			(
-				2u16,
-				pallet_referenda::TrackInfo {
-					name: "proficients",
+			},
+			Track {
+				id: 2u16,
+				info: TrackInfo {
+					name: s("proficients"),
 					max_deciding: 10,
 					decision_deposit: 10 * 3 * CENTS,
 					prepare_period: 30 * MINUTES,
@@ -99,11 +105,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(50),
 					},
 				},
-			),
-			(
-				3u16,
-				pallet_referenda::TrackInfo {
-					name: "fellows",
+			},
+			Track {
+				id: 3u16,
+				info: TrackInfo {
+					name: s("fellows"),
 					max_deciding: 10,
 					decision_deposit: 10 * 3 * CENTS,
 					prepare_period: 30 * MINUTES,
@@ -121,11 +127,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(50),
 					},
 				},
-			),
-			(
-				4u16,
-				pallet_referenda::TrackInfo {
-					name: "senior fellows",
+			},
+			Track {
+				id: 4u16,
+				info: TrackInfo {
+					name: s("senior fellows"),
 					max_deciding: 10,
 					decision_deposit: 10 * 3 * CENTS,
 					prepare_period: 30 * MINUTES,
@@ -143,11 +149,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(50),
 					},
 				},
-			),
-			(
-				5u16,
-				pallet_referenda::TrackInfo {
-					name: "experts",
+			},
+			Track {
+				id: 5u16,
+				info: TrackInfo {
+					name: s("experts"),
 					max_deciding: 10,
 					decision_deposit: 1 * 3 * CENTS,
 					prepare_period: 30 * MINUTES,
@@ -165,11 +171,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(50),
 					},
 				},
-			),
-			(
-				6u16,
-				pallet_referenda::TrackInfo {
-					name: "senior experts",
+			},
+			Track {
+				id: 6u16,
+				info: TrackInfo {
+					name: s("senior experts"),
 					max_deciding: 10,
 					decision_deposit: 1 * 3 * CENTS,
 					prepare_period: 30 * MINUTES,
@@ -187,11 +193,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(50),
 					},
 				},
-			),
-			(
-				7u16,
-				pallet_referenda::TrackInfo {
-					name: "masters",
+			},
+			Track {
+				id: 7u16,
+				info: TrackInfo {
+					name: s("masters"),
 					max_deciding: 10,
 					decision_deposit: 1 * 3 * CENTS,
 					prepare_period: 30 * MINUTES,
@@ -209,11 +215,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(50),
 					},
 				},
-			),
-			(
-				8u16,
-				pallet_referenda::TrackInfo {
-					name: "senior masters",
+			},
+			Track {
+				id: 8u16,
+				info: TrackInfo {
+					name: s("senior masters"),
 					max_deciding: 10,
 					decision_deposit: 1 * 3 * CENTS,
 					prepare_period: 30 * MINUTES,
@@ -231,11 +237,11 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(50),
 					},
 				},
-			),
-			(
-				9u16,
-				pallet_referenda::TrackInfo {
-					name: "grand masters",
+			},
+			Track {
+				id: 9u16,
+				info: TrackInfo {
+					name: s("grand masters"),
 					max_deciding: 10,
 					decision_deposit: 1 * 3 * CENTS,
 					prepare_period: 30 * MINUTES,
@@ -253,9 +259,9 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 						ceil: Perbill::from_percent(50),
 					},
 				},
-			),
+			},
 		];
-		&DATA[..]
+		DATA.iter().map(Cow::Borrowed)
 	}
 	fn track_for(id: &Self::RuntimeOrigin) -> Result<Self::Id, ()> {
 		use super::origins::Origin;
@@ -285,7 +291,6 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 		}
 	}
 }
-pallet_referenda::impl_tracksinfo_get!(TracksInfo, Balance, BlockNumber);
 
 pub type FellowshipReferendaInstance = pallet_referenda::Instance2;
 
diff --git a/polkadot/runtime/rococo/src/governance/tracks.rs b/polkadot/runtime/rococo/src/governance/tracks.rs
index 3765569f183..62229ff5ef5 100644
--- a/polkadot/runtime/rococo/src/governance/tracks.rs
+++ b/polkadot/runtime/rococo/src/governance/tracks.rs
@@ -18,6 +18,9 @@
 
 use super::*;
 
+use alloc::borrow::Cow;
+use sp_runtime::str_array as s;
+
 const fn percent(x: i32) -> sp_arithmetic::FixedI64 {
 	sp_arithmetic::FixedI64::from_rational(x as u128, 100)
 }
@@ -65,11 +68,11 @@ const APP_WHITELISTED_CALLER: Curve =
 const SUP_WHITELISTED_CALLER: Curve =
 	Curve::make_reciprocal(1, 28, percent(20), percent(5), percent(50));
 
-const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15] = [
-	(
-		0,
-		pallet_referenda::TrackInfo {
-			name: "root",
+const TRACKS_DATA: [pallet_referenda::Track<u16, Balance, BlockNumber>; 15] = [
+	pallet_referenda::Track {
+		id: 0,
+		info: pallet_referenda::TrackInfo {
+			name: s("root"),
 			max_deciding: 1,
 			decision_deposit: 100 * GRAND,
 			prepare_period: 8 * MINUTES,
@@ -79,11 +82,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_ROOT,
 			min_support: SUP_ROOT,
 		},
-	),
-	(
-		1,
-		pallet_referenda::TrackInfo {
-			name: "whitelisted_caller",
+	},
+	pallet_referenda::Track {
+		id: 1,
+		info: pallet_referenda::TrackInfo {
+			name: s("whitelisted_caller"),
 			max_deciding: 100,
 			decision_deposit: 10 * GRAND,
 			prepare_period: 6 * MINUTES,
@@ -93,11 +96,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_WHITELISTED_CALLER,
 			min_support: SUP_WHITELISTED_CALLER,
 		},
-	),
-	(
-		10,
-		pallet_referenda::TrackInfo {
-			name: "staking_admin",
+	},
+	pallet_referenda::Track {
+		id: 10,
+		info: pallet_referenda::TrackInfo {
+			name: s("staking_admin"),
 			max_deciding: 10,
 			decision_deposit: 5 * GRAND,
 			prepare_period: 8 * MINUTES,
@@ -107,11 +110,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_STAKING_ADMIN,
 			min_support: SUP_STAKING_ADMIN,
 		},
-	),
-	(
-		11,
-		pallet_referenda::TrackInfo {
-			name: "treasurer",
+	},
+	pallet_referenda::Track {
+		id: 11,
+		info: pallet_referenda::TrackInfo {
+			name: s("treasurer"),
 			max_deciding: 10,
 			decision_deposit: 1 * GRAND,
 			prepare_period: 8 * MINUTES,
@@ -121,11 +124,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_TREASURER,
 			min_support: SUP_TREASURER,
 		},
-	),
-	(
-		12,
-		pallet_referenda::TrackInfo {
-			name: "lease_admin",
+	},
+	pallet_referenda::Track {
+		id: 12,
+		info: pallet_referenda::TrackInfo {
+			name: s("lease_admin"),
 			max_deciding: 10,
 			decision_deposit: 5 * GRAND,
 			prepare_period: 8 * MINUTES,
@@ -135,11 +138,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_LEASE_ADMIN,
 			min_support: SUP_LEASE_ADMIN,
 		},
-	),
-	(
-		13,
-		pallet_referenda::TrackInfo {
-			name: "fellowship_admin",
+	},
+	pallet_referenda::Track {
+		id: 13,
+		info: pallet_referenda::TrackInfo {
+			name: s("fellowship_admin"),
 			max_deciding: 10,
 			decision_deposit: 5 * GRAND,
 			prepare_period: 8 * MINUTES,
@@ -149,11 +152,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_FELLOWSHIP_ADMIN,
 			min_support: SUP_FELLOWSHIP_ADMIN,
 		},
-	),
-	(
-		14,
-		pallet_referenda::TrackInfo {
-			name: "general_admin",
+	},
+	pallet_referenda::Track {
+		id: 14,
+		info: pallet_referenda::TrackInfo {
+			name: s("general_admin"),
 			max_deciding: 10,
 			decision_deposit: 5 * GRAND,
 			prepare_period: 8 * MINUTES,
@@ -163,11 +166,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_GENERAL_ADMIN,
 			min_support: SUP_GENERAL_ADMIN,
 		},
-	),
-	(
-		15,
-		pallet_referenda::TrackInfo {
-			name: "auction_admin",
+	},
+	pallet_referenda::Track {
+		id: 15,
+		info: pallet_referenda::TrackInfo {
+			name: s("auction_admin"),
 			max_deciding: 10,
 			decision_deposit: 5 * GRAND,
 			prepare_period: 8 * MINUTES,
@@ -177,11 +180,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_AUCTION_ADMIN,
 			min_support: SUP_AUCTION_ADMIN,
 		},
-	),
-	(
-		20,
-		pallet_referenda::TrackInfo {
-			name: "referendum_canceller",
+	},
+	pallet_referenda::Track {
+		id: 20,
+		info: pallet_referenda::TrackInfo {
+			name: s("referendum_canceller"),
 			max_deciding: 1_000,
 			decision_deposit: 10 * GRAND,
 			prepare_period: 8 * MINUTES,
@@ -191,11 +194,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_REFERENDUM_CANCELLER,
 			min_support: SUP_REFERENDUM_CANCELLER,
 		},
-	),
-	(
-		21,
-		pallet_referenda::TrackInfo {
-			name: "referendum_killer",
+	},
+	pallet_referenda::Track {
+		id: 21,
+		info: pallet_referenda::TrackInfo {
+			name: s("referendum_killer"),
 			max_deciding: 1_000,
 			decision_deposit: 50 * GRAND,
 			prepare_period: 8 * MINUTES,
@@ -205,11 +208,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_REFERENDUM_KILLER,
 			min_support: SUP_REFERENDUM_KILLER,
 		},
-	),
-	(
-		30,
-		pallet_referenda::TrackInfo {
-			name: "small_tipper",
+	},
+	pallet_referenda::Track {
+		id: 30,
+		info: pallet_referenda::TrackInfo {
+			name: s("small_tipper"),
 			max_deciding: 200,
 			decision_deposit: 1 * 3 * CENTS,
 			prepare_period: 1 * MINUTES,
@@ -219,11 +222,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_SMALL_TIPPER,
 			min_support: SUP_SMALL_TIPPER,
 		},
-	),
-	(
-		31,
-		pallet_referenda::TrackInfo {
-			name: "big_tipper",
+	},
+	pallet_referenda::Track {
+		id: 31,
+		info: pallet_referenda::TrackInfo {
+			name: s("big_tipper"),
 			max_deciding: 100,
 			decision_deposit: 10 * 3 * CENTS,
 			prepare_period: 4 * MINUTES,
@@ -233,11 +236,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_BIG_TIPPER,
 			min_support: SUP_BIG_TIPPER,
 		},
-	),
-	(
-		32,
-		pallet_referenda::TrackInfo {
-			name: "small_spender",
+	},
+	pallet_referenda::Track {
+		id: 32,
+		info: pallet_referenda::TrackInfo {
+			name: s("small_spender"),
 			max_deciding: 50,
 			decision_deposit: 100 * 3 * CENTS,
 			prepare_period: 10 * MINUTES,
@@ -247,11 +250,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_SMALL_SPENDER,
 			min_support: SUP_SMALL_SPENDER,
 		},
-	),
-	(
-		33,
-		pallet_referenda::TrackInfo {
-			name: "medium_spender",
+	},
+	pallet_referenda::Track {
+		id: 33,
+		info: pallet_referenda::TrackInfo {
+			name: s("medium_spender"),
 			max_deciding: 50,
 			decision_deposit: 200 * 3 * CENTS,
 			prepare_period: 10 * MINUTES,
@@ -261,11 +264,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_MEDIUM_SPENDER,
 			min_support: SUP_MEDIUM_SPENDER,
 		},
-	),
-	(
-		34,
-		pallet_referenda::TrackInfo {
-			name: "big_spender",
+	},
+	pallet_referenda::Track {
+		id: 34,
+		info: pallet_referenda::TrackInfo {
+			name: s("big_spender"),
 			max_deciding: 50,
 			decision_deposit: 400 * 3 * CENTS,
 			prepare_period: 10 * MINUTES,
@@ -275,15 +278,18 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_BIG_SPENDER,
 			min_support: SUP_BIG_SPENDER,
 		},
-	),
+	},
 ];
 
 pub struct TracksInfo;
 impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 	type Id = u16;
 	type RuntimeOrigin = <RuntimeOrigin as frame_support::traits::OriginTrait>::PalletsOrigin;
-	fn tracks() -> &'static [(Self::Id, pallet_referenda::TrackInfo<Balance, BlockNumber>)] {
-		&TRACKS_DATA[..]
+
+	fn tracks(
+	) -> impl Iterator<Item = Cow<'static, pallet_referenda::Track<Self::Id, Balance, BlockNumber>>>
+	{
+		TRACKS_DATA.iter().map(Cow::Borrowed)
 	}
 	fn track_for(id: &Self::RuntimeOrigin) -> Result<Self::Id, ()> {
 		if let Ok(system_origin) = frame_system::RawOrigin::try_from(id.clone()) {
@@ -317,4 +323,3 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 		}
 	}
 }
-pallet_referenda::impl_tracksinfo_get!(TracksInfo, Balance, BlockNumber);
diff --git a/polkadot/runtime/westend/src/governance/tracks.rs b/polkadot/runtime/westend/src/governance/tracks.rs
index 3765569f183..62229ff5ef5 100644
--- a/polkadot/runtime/westend/src/governance/tracks.rs
+++ b/polkadot/runtime/westend/src/governance/tracks.rs
@@ -18,6 +18,9 @@
 
 use super::*;
 
+use alloc::borrow::Cow;
+use sp_runtime::str_array as s;
+
 const fn percent(x: i32) -> sp_arithmetic::FixedI64 {
 	sp_arithmetic::FixedI64::from_rational(x as u128, 100)
 }
@@ -65,11 +68,11 @@ const APP_WHITELISTED_CALLER: Curve =
 const SUP_WHITELISTED_CALLER: Curve =
 	Curve::make_reciprocal(1, 28, percent(20), percent(5), percent(50));
 
-const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15] = [
-	(
-		0,
-		pallet_referenda::TrackInfo {
-			name: "root",
+const TRACKS_DATA: [pallet_referenda::Track<u16, Balance, BlockNumber>; 15] = [
+	pallet_referenda::Track {
+		id: 0,
+		info: pallet_referenda::TrackInfo {
+			name: s("root"),
 			max_deciding: 1,
 			decision_deposit: 100 * GRAND,
 			prepare_period: 8 * MINUTES,
@@ -79,11 +82,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_ROOT,
 			min_support: SUP_ROOT,
 		},
-	),
-	(
-		1,
-		pallet_referenda::TrackInfo {
-			name: "whitelisted_caller",
+	},
+	pallet_referenda::Track {
+		id: 1,
+		info: pallet_referenda::TrackInfo {
+			name: s("whitelisted_caller"),
 			max_deciding: 100,
 			decision_deposit: 10 * GRAND,
 			prepare_period: 6 * MINUTES,
@@ -93,11 +96,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_WHITELISTED_CALLER,
 			min_support: SUP_WHITELISTED_CALLER,
 		},
-	),
-	(
-		10,
-		pallet_referenda::TrackInfo {
-			name: "staking_admin",
+	},
+	pallet_referenda::Track {
+		id: 10,
+		info: pallet_referenda::TrackInfo {
+			name: s("staking_admin"),
 			max_deciding: 10,
 			decision_deposit: 5 * GRAND,
 			prepare_period: 8 * MINUTES,
@@ -107,11 +110,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_STAKING_ADMIN,
 			min_support: SUP_STAKING_ADMIN,
 		},
-	),
-	(
-		11,
-		pallet_referenda::TrackInfo {
-			name: "treasurer",
+	},
+	pallet_referenda::Track {
+		id: 11,
+		info: pallet_referenda::TrackInfo {
+			name: s("treasurer"),
 			max_deciding: 10,
 			decision_deposit: 1 * GRAND,
 			prepare_period: 8 * MINUTES,
@@ -121,11 +124,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_TREASURER,
 			min_support: SUP_TREASURER,
 		},
-	),
-	(
-		12,
-		pallet_referenda::TrackInfo {
-			name: "lease_admin",
+	},
+	pallet_referenda::Track {
+		id: 12,
+		info: pallet_referenda::TrackInfo {
+			name: s("lease_admin"),
 			max_deciding: 10,
 			decision_deposit: 5 * GRAND,
 			prepare_period: 8 * MINUTES,
@@ -135,11 +138,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_LEASE_ADMIN,
 			min_support: SUP_LEASE_ADMIN,
 		},
-	),
-	(
-		13,
-		pallet_referenda::TrackInfo {
-			name: "fellowship_admin",
+	},
+	pallet_referenda::Track {
+		id: 13,
+		info: pallet_referenda::TrackInfo {
+			name: s("fellowship_admin"),
 			max_deciding: 10,
 			decision_deposit: 5 * GRAND,
 			prepare_period: 8 * MINUTES,
@@ -149,11 +152,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_FELLOWSHIP_ADMIN,
 			min_support: SUP_FELLOWSHIP_ADMIN,
 		},
-	),
-	(
-		14,
-		pallet_referenda::TrackInfo {
-			name: "general_admin",
+	},
+	pallet_referenda::Track {
+		id: 14,
+		info: pallet_referenda::TrackInfo {
+			name: s("general_admin"),
 			max_deciding: 10,
 			decision_deposit: 5 * GRAND,
 			prepare_period: 8 * MINUTES,
@@ -163,11 +166,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_GENERAL_ADMIN,
 			min_support: SUP_GENERAL_ADMIN,
 		},
-	),
-	(
-		15,
-		pallet_referenda::TrackInfo {
-			name: "auction_admin",
+	},
+	pallet_referenda::Track {
+		id: 15,
+		info: pallet_referenda::TrackInfo {
+			name: s("auction_admin"),
 			max_deciding: 10,
 			decision_deposit: 5 * GRAND,
 			prepare_period: 8 * MINUTES,
@@ -177,11 +180,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_AUCTION_ADMIN,
 			min_support: SUP_AUCTION_ADMIN,
 		},
-	),
-	(
-		20,
-		pallet_referenda::TrackInfo {
-			name: "referendum_canceller",
+	},
+	pallet_referenda::Track {
+		id: 20,
+		info: pallet_referenda::TrackInfo {
+			name: s("referendum_canceller"),
 			max_deciding: 1_000,
 			decision_deposit: 10 * GRAND,
 			prepare_period: 8 * MINUTES,
@@ -191,11 +194,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_REFERENDUM_CANCELLER,
 			min_support: SUP_REFERENDUM_CANCELLER,
 		},
-	),
-	(
-		21,
-		pallet_referenda::TrackInfo {
-			name: "referendum_killer",
+	},
+	pallet_referenda::Track {
+		id: 21,
+		info: pallet_referenda::TrackInfo {
+			name: s("referendum_killer"),
 			max_deciding: 1_000,
 			decision_deposit: 50 * GRAND,
 			prepare_period: 8 * MINUTES,
@@ -205,11 +208,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_REFERENDUM_KILLER,
 			min_support: SUP_REFERENDUM_KILLER,
 		},
-	),
-	(
-		30,
-		pallet_referenda::TrackInfo {
-			name: "small_tipper",
+	},
+	pallet_referenda::Track {
+		id: 30,
+		info: pallet_referenda::TrackInfo {
+			name: s("small_tipper"),
 			max_deciding: 200,
 			decision_deposit: 1 * 3 * CENTS,
 			prepare_period: 1 * MINUTES,
@@ -219,11 +222,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_SMALL_TIPPER,
 			min_support: SUP_SMALL_TIPPER,
 		},
-	),
-	(
-		31,
-		pallet_referenda::TrackInfo {
-			name: "big_tipper",
+	},
+	pallet_referenda::Track {
+		id: 31,
+		info: pallet_referenda::TrackInfo {
+			name: s("big_tipper"),
 			max_deciding: 100,
 			decision_deposit: 10 * 3 * CENTS,
 			prepare_period: 4 * MINUTES,
@@ -233,11 +236,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_BIG_TIPPER,
 			min_support: SUP_BIG_TIPPER,
 		},
-	),
-	(
-		32,
-		pallet_referenda::TrackInfo {
-			name: "small_spender",
+	},
+	pallet_referenda::Track {
+		id: 32,
+		info: pallet_referenda::TrackInfo {
+			name: s("small_spender"),
 			max_deciding: 50,
 			decision_deposit: 100 * 3 * CENTS,
 			prepare_period: 10 * MINUTES,
@@ -247,11 +250,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_SMALL_SPENDER,
 			min_support: SUP_SMALL_SPENDER,
 		},
-	),
-	(
-		33,
-		pallet_referenda::TrackInfo {
-			name: "medium_spender",
+	},
+	pallet_referenda::Track {
+		id: 33,
+		info: pallet_referenda::TrackInfo {
+			name: s("medium_spender"),
 			max_deciding: 50,
 			decision_deposit: 200 * 3 * CENTS,
 			prepare_period: 10 * MINUTES,
@@ -261,11 +264,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_MEDIUM_SPENDER,
 			min_support: SUP_MEDIUM_SPENDER,
 		},
-	),
-	(
-		34,
-		pallet_referenda::TrackInfo {
-			name: "big_spender",
+	},
+	pallet_referenda::Track {
+		id: 34,
+		info: pallet_referenda::TrackInfo {
+			name: s("big_spender"),
 			max_deciding: 50,
 			decision_deposit: 400 * 3 * CENTS,
 			prepare_period: 10 * MINUTES,
@@ -275,15 +278,18 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 15
 			min_approval: APP_BIG_SPENDER,
 			min_support: SUP_BIG_SPENDER,
 		},
-	),
+	},
 ];
 
 pub struct TracksInfo;
 impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 	type Id = u16;
 	type RuntimeOrigin = <RuntimeOrigin as frame_support::traits::OriginTrait>::PalletsOrigin;
-	fn tracks() -> &'static [(Self::Id, pallet_referenda::TrackInfo<Balance, BlockNumber>)] {
-		&TRACKS_DATA[..]
+
+	fn tracks(
+	) -> impl Iterator<Item = Cow<'static, pallet_referenda::Track<Self::Id, Balance, BlockNumber>>>
+	{
+		TRACKS_DATA.iter().map(Cow::Borrowed)
 	}
 	fn track_for(id: &Self::RuntimeOrigin) -> Result<Self::Id, ()> {
 		if let Ok(system_origin) = frame_system::RawOrigin::try_from(id.clone()) {
@@ -317,4 +323,3 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 		}
 	}
 }
-pallet_referenda::impl_tracksinfo_get!(TracksInfo, Balance, BlockNumber);
diff --git a/prdoc/pr_2072.prdoc b/prdoc/pr_2072.prdoc
new file mode 100644
index 00000000000..fdc1a373938
--- /dev/null
+++ b/prdoc/pr_2072.prdoc
@@ -0,0 +1,23 @@
+title: "Return iterator in pallet_referenda::TracksInfo::tracks"
+
+doc:
+  - audience: Runtime Dev
+    description: |
+      Change the return type of the trait method `pallet_referenda::TracksInfo::tracks` to return an 
+      iterator of `Cow<'static, Tracks<_, _, _>>` instead of a static slice in order to support more 
+      flexible implementations that can define referenda tracks dynamically.
+  - audience: Runtime User
+    description: |
+      There is a change in `pallet-referenda`. Now, the tracks are retrieved as a list of `Track`s. Also, the names of
+      the tracks might have some trailing null values (`\0`). This means display representation of the tracks' names
+      must be sanitized.
+
+crates:
+  - name: pallet-referenda
+    bump: major
+  - name: westend-runtime
+    bump: major
+  - name: rococo-runtime
+    bump: major
+  - name: collectives-westend-runtime
+    bump: major
\ No newline at end of file
diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs
index e0404fdc2bc..bebf48618ba 100644
--- a/substrate/bin/node/runtime/src/lib.rs
+++ b/substrate/bin/node/runtime/src/lib.rs
@@ -107,7 +107,7 @@ use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H160};
 use sp_inherents::{CheckInherentsResult, InherentData};
 use sp_runtime::{
 	curve::PiecewiseLinear,
-	generic, impl_opaque_keys,
+	generic, impl_opaque_keys, str_array as s,
 	traits::{
 		self, AccountIdConversion, BlakeTwo256, Block as BlockT, Bounded, ConvertInto,
 		MaybeConvert, NumberFor, OpaqueKeys, SaturatedConversion, StaticLookup,
@@ -116,6 +116,7 @@ use sp_runtime::{
 	ApplyExtrinsicResult, FixedPointNumber, FixedU128, MultiSignature, MultiSigner, Perbill,
 	Percent, Permill, Perquintill, RuntimeDebug,
 };
+use sp_std::{borrow::Cow, prelude::*};
 #[cfg(any(feature = "std", test))]
 use sp_version::NativeVersion;
 use sp_version::RuntimeVersion;
@@ -1280,43 +1281,20 @@ pub struct TracksInfo;
 impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
 	type Id = u16;
 	type RuntimeOrigin = <RuntimeOrigin as frame_support::traits::OriginTrait>::PalletsOrigin;
-	fn tracks() -> &'static [(Self::Id, pallet_referenda::TrackInfo<Balance, BlockNumber>)] {
-		static DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 1] = [(
-			0u16,
-			pallet_referenda::TrackInfo {
-				name: "root",
-				max_deciding: 1,
-				decision_deposit: 10,
-				prepare_period: 4,
-				decision_period: 4,
-				confirm_period: 2,
-				min_enactment_period: 4,
-				min_approval: pallet_referenda::Curve::LinearDecreasing {
-					length: Perbill::from_percent(100),
-					floor: Perbill::from_percent(50),
-					ceil: Perbill::from_percent(100),
-				},
-				min_support: pallet_referenda::Curve::LinearDecreasing {
-					length: Perbill::from_percent(100),
-					floor: Perbill::from_percent(0),
-					ceil: Perbill::from_percent(100),
-				},
-			},
-		)];
-		&DATA[..]
+
+	fn tracks(
+	) -> impl Iterator<Item = Cow<'static, pallet_referenda::Track<Self::Id, Balance, BlockNumber>>>
+	{
+		dynamic_params::referenda::Tracks::get().into_iter().map(Cow::Owned)
 	}
 	fn track_for(id: &Self::RuntimeOrigin) -> Result<Self::Id, ()> {
-		if let Ok(system_origin) = frame_system::RawOrigin::try_from(id.clone()) {
-			match system_origin {
-				frame_system::RawOrigin::Root => Ok(0),
-				_ => Err(()),
-			}
-		} else {
-			Err(())
-		}
+		dynamic_params::referenda::Origins::get()
+			.iter()
+			.find(|(o, _)| id == o)
+			.map(|(_, track_id)| *track_id)
+			.ok_or(())
 	}
 }
-pallet_referenda::impl_tracksinfo_get!(TracksInfo, Balance, BlockNumber);
 
 impl pallet_referenda::Config for Runtime {
 	type WeightInfo = pallet_referenda::weights::SubstrateWeight<Self>;
@@ -2676,6 +2654,46 @@ pub mod dynamic_params {
 		#[codec(index = 1)]
 		pub static ByteDeposit: Balance = 1 * CENTS;
 	}
+
+	#[dynamic_pallet_params]
+	#[codec(index = 1)]
+	pub mod referenda {
+		/// The configuration for the tracks
+		#[codec(index = 0)]
+		pub static Tracks: BoundedVec<
+			pallet_referenda::Track<u16, Balance, BlockNumber>,
+			ConstU32<100>,
+		> = BoundedVec::truncate_from(vec![pallet_referenda::Track {
+			id: 0u16,
+			info: pallet_referenda::TrackInfo {
+				name: s("root"),
+				max_deciding: 1,
+				decision_deposit: 10,
+				prepare_period: 4,
+				decision_period: 4,
+				confirm_period: 2,
+				min_enactment_period: 4,
+				min_approval: pallet_referenda::Curve::LinearDecreasing {
+					length: Perbill::from_percent(100),
+					floor: Perbill::from_percent(50),
+					ceil: Perbill::from_percent(100),
+				},
+				min_support: pallet_referenda::Curve::LinearDecreasing {
+					length: Perbill::from_percent(100),
+					floor: Perbill::from_percent(0),
+					ceil: Perbill::from_percent(100),
+				},
+			},
+		}]);
+
+		/// A list mapping every origin with a track Id
+		#[codec(index = 1)]
+		pub static Origins: BoundedVec<(OriginCaller, u16), ConstU32<100>> =
+			BoundedVec::truncate_from(vec![(
+				OriginCaller::system(frame_system::RawOrigin::Root),
+				0,
+			)]);
+	}
 }
 
 #[cfg(feature = "runtime-benchmarks")]
@@ -2701,6 +2719,10 @@ impl EnsureOriginWithArg<RuntimeOrigin, RuntimeParametersKey> for DynamicParamet
 				frame_system::ensure_root(origin.clone()).map_err(|_| origin)?;
 				return Ok(())
 			},
+			RuntimeParametersKey::Referenda(_) => {
+				frame_system::ensure_root(origin.clone()).map_err(|_| origin)?;
+				return Ok(())
+			},
 		}
 	}
 
diff --git a/substrate/frame/referenda/src/benchmarking.rs b/substrate/frame/referenda/src/benchmarking.rs
index 895f95dbec5..59499d9c8bf 100644
--- a/substrate/frame/referenda/src/benchmarking.rs
+++ b/substrate/frame/referenda/src/benchmarking.rs
@@ -19,7 +19,7 @@
 
 use super::*;
 use crate::Pallet as Referenda;
-use alloc::{vec, vec::Vec};
+use alloc::{borrow::Cow, vec, vec::Vec};
 use assert_matches::assert_matches;
 use frame_benchmarking::v1::{
 	account, benchmarks_instance_pallet, whitelist_account, BenchmarkError,
@@ -110,7 +110,7 @@ fn fill_queue<T: Config<I>, I: 'static>(
 	others
 }
 
-fn info<T: Config<I>, I: 'static>(index: ReferendumIndex) -> &'static TrackInfoOf<T, I> {
+fn info<T: Config<I>, I: 'static>(index: ReferendumIndex) -> Cow<'static, TrackInfoOf<T, I>> {
 	let status = Referenda::<T, I>::ensure_ongoing(index).unwrap();
 	T::Tracks::info(status.track).expect("Id value returned from T::Tracks")
 }
diff --git a/substrate/frame/referenda/src/lib.rs b/substrate/frame/referenda/src/lib.rs
index e6a895f9c59..b58baa341cf 100644
--- a/substrate/frame/referenda/src/lib.rs
+++ b/substrate/frame/referenda/src/lib.rs
@@ -100,7 +100,7 @@ pub use self::{
 		BalanceOf, BlockNumberFor, BoundedCallOf, CallOf, Curve, DecidingStatus, DecidingStatusOf,
 		Deposit, InsertSorted, NegativeImbalanceOf, PalletsOriginOf, ReferendumIndex,
 		ReferendumInfo, ReferendumInfoOf, ReferendumStatus, ReferendumStatusOf, ScheduleAddressOf,
-		TallyOf, TrackIdOf, TrackInfo, TrackInfoOf, TracksInfo, VotesOf,
+		TallyOf, Track, TrackIdOf, TrackInfo, TrackInfoOf, TracksInfo, VotesOf,
 	},
 	weights::WeightInfo,
 };
@@ -117,27 +117,6 @@ pub mod benchmarking;
 
 pub use frame_support::traits::Get;
 
-#[macro_export]
-macro_rules! impl_tracksinfo_get {
-	($tracksinfo:ty, $balance:ty, $blocknumber:ty) => {
-		impl
-			$crate::Get<
-				$crate::Vec<(
-					<$tracksinfo as $crate::TracksInfo<$balance, $blocknumber>>::Id,
-					$crate::TrackInfo<$balance, $blocknumber>,
-				)>,
-			> for $tracksinfo
-		{
-			fn get() -> $crate::Vec<(
-				<$tracksinfo as $crate::TracksInfo<$balance, $blocknumber>>::Id,
-				$crate::TrackInfo<$balance, $blocknumber>,
-			)> {
-				<$tracksinfo as $crate::TracksInfo<$balance, $blocknumber>>::tracks().to_vec()
-			}
-		}
-	};
-}
-
 const ASSEMBLY_ID: LockIdentifier = *b"assembly";
 
 #[frame_support::pallet]
@@ -228,17 +207,11 @@ pub mod pallet {
 
 		// The other stuff.
 		/// Information concerning the different referendum tracks.
-		#[pallet::constant]
-		type Tracks: Get<
-				Vec<(
-					<Self::Tracks as TracksInfo<BalanceOf<Self, I>, BlockNumberFor<Self, I>>>::Id,
-					TrackInfo<BalanceOf<Self, I>, BlockNumberFor<Self, I>>,
-				)>,
-			> + TracksInfo<
-				BalanceOf<Self, I>,
-				BlockNumberFor<Self, I>,
-				RuntimeOrigin = <Self::RuntimeOrigin as OriginTrait>::PalletsOrigin,
-			>;
+		type Tracks: TracksInfo<
+			BalanceOf<Self, I>,
+			BlockNumberFor<Self, I>,
+			RuntimeOrigin = <Self::RuntimeOrigin as OriginTrait>::PalletsOrigin,
+		>;
 
 		/// The preimage provider.
 		type Preimages: QueryPreimage<H = Self::Hashing> + StorePreimage;
@@ -249,6 +222,14 @@ pub mod pallet {
 		type BlockNumberProvider: BlockNumberProvider;
 	}
 
+	#[pallet::extra_constants]
+	impl<T: Config<I>, I: 'static> Pallet<T, I> {
+		#[pallet::constant_name(Tracks)]
+		fn tracks() -> Vec<Track<TrackIdOf<T, I>, BalanceOf<T, I>, BlockNumberFor<T, I>>> {
+			T::Tracks::tracks().map(|t| t.into_owned()).collect()
+		}
+	}
+
 	/// The next free referendum index, aka the number of referenda started so far.
 	#[pallet::storage]
 	pub type ReferendumCount<T, I = ()> = StorageValue<_, ReferendumIndex, ValueQuery>;
@@ -532,7 +513,7 @@ pub mod pallet {
 			let who = ensure_signed(origin)?;
 			let mut status = Self::ensure_ongoing(index)?;
 			ensure!(status.decision_deposit.is_none(), Error::<T, I>::HasDeposit);
-			let track = Self::track(status.track).ok_or(Error::<T, I>::NoTrack)?;
+			let track = T::Tracks::info(status.track).ok_or(Error::<T, I>::NoTrack)?;
 			status.decision_deposit =
 				Some(Self::take_deposit(who.clone(), track.decision_deposit)?);
 			let now = T::BlockNumberProvider::current_block_number();
@@ -668,7 +649,7 @@ pub mod pallet {
 				if let Some((index, mut status)) = Self::next_for_deciding(&mut track_queue) {
 					let now = T::BlockNumberProvider::current_block_number();
 					let (maybe_alarm, branch) =
-						Self::begin_deciding(&mut status, index, now, track_info);
+						Self::begin_deciding(&mut status, index, now, &track_info);
 					if let Some(set_alarm) = maybe_alarm {
 						Self::ensure_alarm_at(&mut status, index, set_alarm);
 					}
@@ -756,7 +737,7 @@ impl<T: Config<I>, I: 'static> Polling<T::Tally> for Pallet<T, I> {
 	type Class = TrackIdOf<T, I>;
 
 	fn classes() -> Vec<Self::Class> {
-		T::Tracks::tracks().iter().map(|x| x.0).collect()
+		T::Tracks::track_ids().collect()
 	}
 
 	fn access_poll<R>(
@@ -849,10 +830,9 @@ impl<T: Config<I>, I: 'static> Polling<T::Tally> for Pallet<T, I> {
 	#[cfg(feature = "runtime-benchmarks")]
 	fn max_ongoing() -> (Self::Class, u32) {
 		let r = T::Tracks::tracks()
-			.iter()
-			.max_by_key(|(_, info)| info.max_deciding)
+			.max_by_key(|t| t.info.max_deciding)
 			.expect("Always one class");
-		(r.0, r.1.max_deciding)
+		(r.id, r.info.max_deciding)
 	}
 }
 
@@ -874,7 +854,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
 		let info = ReferendumInfoFor::<T, I>::get(ref_index).ok_or(Error::<T, I>::BadReferendum)?;
 		match info {
 			ReferendumInfo::Ongoing(status) => {
-				let track = Self::track(status.track).ok_or(Error::<T, I>::NoTrack)?;
+				let track = T::Tracks::info(status.track).ok_or(Error::<T, I>::NoTrack)?;
 				let elapsed = if let Some(deciding) = status.deciding {
 					T::BlockNumberProvider::current_block_number().saturating_sub(deciding.since)
 				} else {
@@ -1104,7 +1084,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
 	) -> (ReferendumInfoOf<T, I>, bool, ServiceBranch) {
 		let mut dirty = false;
 		// Should it begin being decided?
-		let track = match Self::track(status.track) {
+		let track = match T::Tracks::info(status.track) {
 			Some(x) => x,
 			None => return (ReferendumInfo::Ongoing(status), false, ServiceBranch::Fail),
 		};
@@ -1140,7 +1120,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
 						let prepare_end = status.submitted.saturating_add(track.prepare_period);
 						if now >= prepare_end {
 							let (maybe_alarm, branch) =
-								Self::ready_for_deciding(now, track, index, &mut status);
+								Self::ready_for_deciding(now, &track, index, &mut status);
 							if let Some(set_alarm) = maybe_alarm {
 								alarm = alarm.min(set_alarm);
 							}
@@ -1187,7 +1167,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
 							Self::ensure_no_alarm(&mut status);
 							Self::note_one_fewer_deciding(status.track);
 							let (desired, call) = (status.enactment, status.proposal);
-							Self::schedule_enactment(index, track, desired, status.origin, call);
+							Self::schedule_enactment(index, &track, desired, status.origin, call);
 							Self::deposit_event(Event::<T, I>::Confirmed {
 								index,
 								tally: status.tally,
@@ -1237,7 +1217,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
 						ServiceBranch::ContinueNotConfirming
 					}
 				};
-				alarm = Self::decision_time(deciding, &status.tally, status.track, track);
+				alarm = Self::decision_time(deciding, &status.tally, status.track, &track);
 			},
 		}
 
@@ -1303,13 +1283,6 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
 		}
 	}
 
-	/// Get the track info value for the track `id`.
-	fn track(id: TrackIdOf<T, I>) -> Option<&'static TrackInfoOf<T, I>> {
-		let tracks = T::Tracks::tracks();
-		let index = tracks.binary_search_by_key(&id, |x| x.0).unwrap_or_else(|x| x);
-		Some(&tracks[index].1)
-	}
-
 	/// Determine whether the given `tally` would result in a referendum passing at `elapsed` blocks
 	/// into a total decision `period`, given the two curves for `support_needed` and
 	/// `approval_needed`.
@@ -1378,7 +1351,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
 			match referendum {
 				ReferendumInfo::Ongoing(status) => {
 					ensure!(
-						Self::track(status.track).is_some(),
+						T::Tracks::info(status.track).is_some(),
 						"No track info for the track of the referendum."
 					);
 
@@ -1404,8 +1377,8 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
 	///  [`ReferendumInfoFor`] storage map.
 	#[cfg(any(feature = "try-runtime", test))]
 	fn try_state_tracks() -> Result<(), sp_runtime::TryRuntimeError> {
-		T::Tracks::tracks().iter().try_for_each(|track| {
-			TrackQueue::<T, I>::get(track.0).iter().try_for_each(
+		T::Tracks::tracks().try_for_each(|track| {
+			TrackQueue::<T, I>::get(track.id).iter().try_for_each(
 				|(referendum_index, _)| -> Result<(), sp_runtime::TryRuntimeError> {
 					ensure!(
 					ReferendumInfoFor::<T, I>::contains_key(referendum_index),
diff --git a/substrate/frame/referenda/src/mock.rs b/substrate/frame/referenda/src/mock.rs
index 10e5f35bbab..5c9c12dee44 100644
--- a/substrate/frame/referenda/src/mock.rs
+++ b/substrate/frame/referenda/src/mock.rs
@@ -18,7 +18,8 @@
 //! The crate's tests.
 
 use super::*;
-use crate as pallet_referenda;
+use crate::{self as pallet_referenda, types::Track};
+use alloc::borrow::Cow;
 use codec::{Decode, Encode, MaxEncodedLen};
 use frame_support::{
 	assert_ok, derive_impl, ord_parameter_types, parameter_types,
@@ -29,6 +30,7 @@ use frame_support::{
 };
 use frame_system::{EnsureRoot, EnsureSignedBy};
 use sp_runtime::{
+	str_array as s,
 	traits::{BlakeTwo256, Hash},
 	BuildStorage, DispatchResult, Perbill,
 };
@@ -103,12 +105,13 @@ pub struct TestTracksInfo;
 impl TracksInfo<u64, u64> for TestTracksInfo {
 	type Id = u8;
 	type RuntimeOrigin = <RuntimeOrigin as OriginTrait>::PalletsOrigin;
-	fn tracks() -> &'static [(Self::Id, TrackInfo<u64, u64>)] {
-		static DATA: [(u8, TrackInfo<u64, u64>); 3] = [
-			(
-				0u8,
-				TrackInfo {
-					name: "root",
+
+	fn tracks() -> impl Iterator<Item = Cow<'static, Track<Self::Id, u64, u64>>> {
+		static DATA: [Track<u8, u64, u64>; 3] = [
+			Track {
+				id: 0u8,
+				info: TrackInfo {
+					name: s("root"),
 					max_deciding: 1,
 					decision_deposit: 10,
 					prepare_period: 4,
@@ -126,11 +129,11 @@ impl TracksInfo<u64, u64> for TestTracksInfo {
 						ceil: Perbill::from_percent(100),
 					},
 				},
-			),
-			(
-				1u8,
-				TrackInfo {
-					name: "none",
+			},
+			Track {
+				id: 1u8,
+				info: TrackInfo {
+					name: s("none"),
 					max_deciding: 3,
 					decision_deposit: 1,
 					prepare_period: 2,
@@ -148,11 +151,11 @@ impl TracksInfo<u64, u64> for TestTracksInfo {
 						ceil: Perbill::from_percent(100),
 					},
 				},
-			),
-			(
-				2u8,
-				TrackInfo {
-					name: "none",
+			},
+			Track {
+				id: 2u8,
+				info: TrackInfo {
+					name: s("none"),
 					max_deciding: 3,
 					decision_deposit: 1,
 					prepare_period: 2,
@@ -170,9 +173,9 @@ impl TracksInfo<u64, u64> for TestTracksInfo {
 						ceil: Perbill::from_percent(100),
 					},
 				},
-			),
+			},
 		];
-		&DATA[..]
+		DATA.iter().map(Cow::Borrowed)
 	}
 	fn track_for(id: &Self::RuntimeOrigin) -> Result<Self::Id, ()> {
 		if let Ok(system_origin) = frame_system::RawOrigin::try_from(id.clone()) {
@@ -187,7 +190,6 @@ impl TracksInfo<u64, u64> for TestTracksInfo {
 		}
 	}
 }
-impl_tracksinfo_get!(TestTracksInfo, u64, u64);
 
 impl Config for Test {
 	type WeightInfo = ();
diff --git a/substrate/frame/referenda/src/tests.rs b/substrate/frame/referenda/src/tests.rs
index 9f851992496..d556d10c44a 100644
--- a/substrate/frame/referenda/src/tests.rs
+++ b/substrate/frame/referenda/src/tests.rs
@@ -289,11 +289,12 @@ fn alarm_interval_works() {
 fn decision_time_is_correct() {
 	ExtBuilder::default().build_and_execute(|| {
 		let decision_time = |since: u64| {
+			let track = TestTracksInfo::tracks().next().unwrap();
 			Pallet::<Test>::decision_time(
 				&DecidingStatus { since: since.into(), confirming: None },
 				&Tally { ayes: 100, nays: 5 },
-				TestTracksInfo::tracks()[0].0,
-				&TestTracksInfo::tracks()[0].1,
+				track.id,
+				&track.info,
 			)
 		};
 
diff --git a/substrate/frame/referenda/src/types.rs b/substrate/frame/referenda/src/types.rs
index e97e7cc8df6..6a1eb8e82e4 100644
--- a/substrate/frame/referenda/src/types.rs
+++ b/substrate/frame/referenda/src/types.rs
@@ -18,6 +18,7 @@
 //! Miscellaneous additional datatypes.
 
 use super::*;
+use alloc::borrow::Cow;
 use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
 use core::fmt::Debug;
 use frame_support::{
@@ -115,10 +116,13 @@ pub struct Deposit<AccountId, Balance> {
 	pub amount: Balance,
 }
 
-#[derive(Clone, Encode, TypeInfo)]
-pub struct TrackInfo<Balance, Moment> {
+pub const DEFAULT_MAX_TRACK_NAME_LEN: usize = 25;
+
+/// Detailed information about the configuration of a referenda track
+#[derive(Clone, Encode, Decode, MaxEncodedLen, TypeInfo, Eq, PartialEq, Debug)]
+pub struct TrackInfo<Balance, Moment, const N: usize = DEFAULT_MAX_TRACK_NAME_LEN> {
 	/// Name of this track.
-	pub name: &'static str,
+	pub name: [u8; N],
 	/// A limit for the number of referenda on this track that can be being decided at once.
 	/// For Root origin this should generally be just one.
 	pub max_deciding: u32,
@@ -140,42 +144,67 @@ pub struct TrackInfo<Balance, Moment> {
 	pub min_support: Curve,
 }
 
+/// Track groups the information of a voting track with its corresponding identifier
+#[derive(Clone, Encode, Decode, MaxEncodedLen, TypeInfo, Eq, PartialEq, Debug)]
+pub struct Track<Id, Balance, Moment, const N: usize = DEFAULT_MAX_TRACK_NAME_LEN> {
+	pub id: Id,
+	pub info: TrackInfo<Balance, Moment, N>,
+}
+
 /// Information on the voting tracks.
-pub trait TracksInfo<Balance, Moment> {
+pub trait TracksInfo<Balance, Moment, const N: usize = DEFAULT_MAX_TRACK_NAME_LEN>
+where
+	Balance: Clone + Debug + Eq + 'static,
+	Moment: Clone + Debug + Eq + 'static,
+{
 	/// The identifier for a track.
 	type Id: Copy + Parameter + Ord + PartialOrd + Send + Sync + 'static + MaxEncodedLen;
 
 	/// The origin type from which a track is implied.
 	type RuntimeOrigin;
 
-	/// Sorted array of known tracks and their information.
+	/// Return the sorted iterable list of known tracks and their information.
 	///
-	/// The array MUST be sorted by `Id`. Consumers of this trait are advised to assert
+	/// The iterator MUST be sorted by `Id`. Consumers of this trait are advised to assert
 	/// [`Self::check_integrity`] prior to any use.
-	fn tracks() -> &'static [(Self::Id, TrackInfo<Balance, Moment>)];
+	fn tracks() -> impl Iterator<Item = Cow<'static, Track<Self::Id, Balance, Moment, N>>>;
 
 	/// Determine the voting track for the given `origin`.
 	fn track_for(origin: &Self::RuntimeOrigin) -> Result<Self::Id, ()>;
 
-	/// Return the track info for track `id`, by default this just looks it up in `Self::tracks()`.
-	fn info(id: Self::Id) -> Option<&'static TrackInfo<Balance, Moment>> {
-		let tracks = Self::tracks();
-		let maybe_index = tracks.binary_search_by_key(&id, |t| t.0).ok()?;
+	/// Return the list of identifiers of the known tracks.
+	fn track_ids() -> impl Iterator<Item = Self::Id> {
+		Self::tracks().map(|x| x.id)
+	}
 
-		tracks.get(maybe_index).map(|(_, info)| info)
+	/// Return the track info for track `id`, by default this just looks it up in `Self::tracks()`.
+	fn info(id: Self::Id) -> Option<Cow<'static, TrackInfo<Balance, Moment, N>>> {
+		Self::tracks().find(|x| x.id == id).map(|t| match t {
+			Cow::Borrowed(x) => Cow::Borrowed(&x.info),
+			Cow::Owned(x) => Cow::Owned(x.info),
+		})
 	}
 
 	/// Check assumptions about the static data that this trait provides.
-	fn check_integrity() -> Result<(), &'static str>
-	where
-		Balance: 'static,
-		Moment: 'static,
-	{
-		if Self::tracks().windows(2).all(|w| w[0].0 < w[1].0) {
-			Ok(())
-		} else {
-			Err("The tracks that were returned by `tracks` were not sorted by `Id`")
-		}
+	fn check_integrity() -> Result<(), &'static str> {
+		use core::cmp::Ordering;
+		// Adapted from Iterator::is_sorted implementation available in nightly
+		// https://github.com/rust-lang/rust/issues/53485
+		let mut iter = Self::tracks();
+		let mut last = match iter.next() {
+			Some(ref e) => e.id,
+			None => return Ok(()),
+		};
+		iter.all(|curr| {
+			let curr = curr.as_ref().id;
+			if let Ordering::Greater = last.cmp(&curr) {
+				return false;
+			}
+			last = curr;
+			true
+		})
+		.then_some(())
+		.ok_or("The tracks that were returned by `tracks` were not sorted by `Id`")
 	}
 }
 
@@ -551,7 +580,7 @@ impl Debug for Curve {
 mod tests {
 	use super::*;
 	use frame_support::traits::ConstU32;
-	use sp_runtime::PerThing;
+	use sp_runtime::{str_array as s, PerThing};
 
 	const fn percent(x: u128) -> FixedI64 {
 		FixedI64::from_rational(x, 100)
@@ -703,12 +732,12 @@ mod tests {
 		impl TracksInfo<u64, u64> for BadTracksInfo {
 			type Id = u8;
 			type RuntimeOrigin = <RuntimeOrigin as OriginTrait>::PalletsOrigin;
-			fn tracks() -> &'static [(Self::Id, TrackInfo<u64, u64>)] {
-				static DATA: [(u8, TrackInfo<u64, u64>); 2] = [
-					(
-						1u8,
-						TrackInfo {
-							name: "root",
+			fn tracks() -> impl Iterator<Item = Cow<'static, Track<Self::Id, u64, u64>>> {
+				static DATA: [Track<u8, u64, u64>; 2] = [
+					Track {
+						id: 1u8,
+						info: TrackInfo {
+							name: s("root"),
 							max_deciding: 1,
 							decision_deposit: 10,
 							prepare_period: 4,
@@ -726,11 +755,11 @@ mod tests {
 								ceil: Perbill::from_percent(100),
 							},
 						},
-					),
-					(
-						0u8,
-						TrackInfo {
-							name: "none",
+					},
+					Track {
+						id: 0u8,
+						info: TrackInfo {
+							name: s("none"),
 							max_deciding: 3,
 							decision_deposit: 1,
 							prepare_period: 2,
@@ -748,9 +777,9 @@ mod tests {
 								ceil: Perbill::from_percent(100),
 							},
 						},
-					),
+					},
 				];
-				&DATA[..]
+				DATA.iter().map(Cow::Borrowed)
 			}
 			fn track_for(_: &Self::RuntimeOrigin) -> Result<Self::Id, ()> {
 				unimplemented!()
-- 
GitLab