From 3bff6ee7498b190412dab2aadb4d823ddb80112b Mon Sep 17 00:00:00 2001
From: Shaopeng Wang <spxwang@gmail.com>
Date: Tue, 19 Jan 2021 17:24:34 +1300
Subject: [PATCH] Migrate oracle & nft pallet (#357)

* Migrate oracle module.

* Migrate NFT module.

* Impl genesis config default under std.
---
 nft/src/lib.rs                     | 424 ++++++++++++++-------------
 oracle/src/default_combine_data.rs |   8 +-
 oracle/src/lib.rs                  | 446 ++++++++++++++++-------------
 oracle/src/mock.rs                 |   5 +-
 oracle/src/tests.rs                |   8 +-
 utilities/src/ordered_set.rs       |   2 +-
 6 files changed, 483 insertions(+), 410 deletions(-)

diff --git a/nft/src/lib.rs b/nft/src/lib.rs
index 3b43b09..e0e1cb9 100644
--- a/nft/src/lib.rs
+++ b/nft/src/lib.rs
@@ -19,56 +19,81 @@
 //! - `destroy_class` - Destroy NFT(non fungible token) class
 
 #![cfg_attr(not(feature = "std"), no_std)]
-
-use codec::{Decode, Encode};
-use frame_support::{decl_error, decl_module, decl_storage, ensure, Parameter};
-use sp_runtime::{
-	traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedSub, Member, One, Zero},
-	DispatchError, DispatchResult, RuntimeDebug,
-};
-use sp_std::vec::Vec;
+#![allow(clippy::unused_unit)]
 
 mod mock;
 mod tests;
 
-/// Class info
-#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug)]
-pub struct ClassInfo<TokenId, AccountId, Data> {
-	/// Class metadata
-	pub metadata: Vec<u8>,
-	/// Total issuance for the class
-	pub total_issuance: TokenId,
-	/// Class owner
-	pub owner: AccountId,
-	/// Class Properties
-	pub data: Data,
-}
+pub use module::*;
+
+#[frame_support::pallet]
+pub mod module {
+	use codec::{Decode, Encode};
+	use frame_support::{ensure, pallet_prelude::*, Parameter};
+	use sp_runtime::{
+		traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedSub, MaybeSerializeDeserialize, Member, One, Zero},
+		DispatchError, DispatchResult, RuntimeDebug,
+	};
+	use sp_std::vec::Vec;
+
+	/// Class info
+	#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug)]
+	pub struct ClassInfo<TokenId, AccountId, Data> {
+		/// Class metadata
+		pub metadata: Vec<u8>,
+		/// Total issuance for the class
+		pub total_issuance: TokenId,
+		/// Class owner
+		pub owner: AccountId,
+		/// Class Properties
+		pub data: Data,
+	}
 
-/// Token info
-#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug)]
-pub struct TokenInfo<AccountId, Data> {
-	/// Token metadata
-	pub metadata: Vec<u8>,
-	/// Token owner
-	pub owner: AccountId,
-	/// Token Properties
-	pub data: Data,
-}
+	/// Token info
+	#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug)]
+	pub struct TokenInfo<AccountId, Data> {
+		/// Token metadata
+		pub metadata: Vec<u8>,
+		/// Token owner
+		pub owner: AccountId,
+		/// Token Properties
+		pub data: Data,
+	}
 
-pub trait Config: frame_system::Config {
-	/// The class ID type
-	type ClassId: Parameter + Member + AtLeast32BitUnsigned + Default + Copy;
-	/// The token ID type
-	type TokenId: Parameter + Member + AtLeast32BitUnsigned + Default + Copy;
-	/// The class properties type
-	type ClassData: Parameter + Member;
-	/// The token properties type
-	type TokenData: Parameter + Member;
-}
+	#[pallet::config]
+	pub trait Config: frame_system::Config {
+		/// The class ID type
+		type ClassId: Parameter + Member + AtLeast32BitUnsigned + Default + Copy;
+		/// The token ID type
+		type TokenId: Parameter + Member + AtLeast32BitUnsigned + Default + Copy;
+		/// The class properties type
+		type ClassData: Parameter + Member + MaybeSerializeDeserialize;
+		/// The token properties type
+		type TokenData: Parameter + Member + MaybeSerializeDeserialize;
+	}
+
+	pub type ClassInfoOf<T> =
+		ClassInfo<<T as Config>::TokenId, <T as frame_system::Config>::AccountId, <T as Config>::ClassData>;
+	pub type TokenInfoOf<T> = TokenInfo<<T as frame_system::Config>::AccountId, <T as Config>::TokenData>;
+
+	pub type GenesisTokenData<T> = (
+		<T as frame_system::Config>::AccountId, // Token owner
+		Vec<u8>,                                // Token metadata
+		<T as Config>::TokenData,
+	);
+	pub type GenesisTokens<T> = (
+		<T as frame_system::Config>::AccountId, // Token class owner
+		Vec<u8>,                                // Token class metadata
+		<T as Config>::ClassData,
+		Vec<GenesisTokenData<T>>, // Vector of tokens belonging to this class
+	);
+
+	#[pallet::pallet]
+	pub struct Pallet<T>(PhantomData<T>);
 
-decl_error! {
 	/// Error for non-fungible-token module.
-	pub enum Error for Module<T: Config> {
+	#[pallet::error]
+	pub enum Error<T> {
 		/// No available class ID
 		NoAvailableClassId,
 		/// No available token ID
@@ -85,182 +110,193 @@ decl_error! {
 		/// Total issuance is not 0
 		CannotDestroyClass,
 	}
-}
 
-pub type ClassInfoOf<T> =
-	ClassInfo<<T as Config>::TokenId, <T as frame_system::Config>::AccountId, <T as Config>::ClassData>;
-pub type TokenInfoOf<T> = TokenInfo<<T as frame_system::Config>::AccountId, <T as Config>::TokenData>;
-
-pub type GenesisTokenData<T> = (
-	<T as frame_system::Config>::AccountId, // Token owner
-	Vec<u8>,                                // Token metadata
-	<T as Config>::TokenData,
-);
-pub type GenesisTokens<T> = (
-	<T as frame_system::Config>::AccountId, // Token class owner
-	Vec<u8>,                                // Token class metadata
-	<T as Config>::ClassData,
-	Vec<GenesisTokenData<T>>, // Vector of tokens belonging to this class
-);
-
-decl_storage! {
-	trait Store for Module<T: Config> as NonFungibleToken {
-		/// Next available class ID.
-		pub NextClassId get(fn next_class_id): T::ClassId;
-		/// Next available token ID.
-		pub NextTokenId get(fn next_token_id): map hasher(twox_64_concat) T::ClassId => T::TokenId;
-		/// Store class info.
-		///
-		/// Returns `None` if class info not set or removed.
-		pub Classes get(fn classes): map hasher(twox_64_concat) T::ClassId => Option<ClassInfoOf<T>>;
-		/// Store token info.
-		///
-		/// Returns `None` if token info not set or removed.
-		pub Tokens get(fn tokens): double_map hasher(twox_64_concat) T::ClassId, hasher(twox_64_concat) T::TokenId => Option<TokenInfoOf<T>>;
-		/// Token existence check by owner and class ID.
-		#[cfg(not(feature = "disable-tokens-by-owner"))]
-		pub TokensByOwner get(fn tokens_by_owner): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) (T::ClassId, T::TokenId) => Option<()>;
+	/// Next available class ID.
+	#[pallet::storage]
+	#[pallet::getter(fn next_class_id)]
+	pub type NextClassId<T: Config> = StorageValue<_, T::ClassId, ValueQuery>;
+
+	/// Next available token ID.
+	#[pallet::storage]
+	#[pallet::getter(fn next_token_id)]
+	pub type NextTokenId<T: Config> = StorageMap<_, Twox64Concat, T::ClassId, T::TokenId, ValueQuery>;
+
+	/// Store class info.
+	///
+	/// Returns `None` if class info not set or removed.
+	#[pallet::storage]
+	#[pallet::getter(fn classes)]
+	pub type Classes<T: Config> = StorageMap<_, Twox64Concat, T::ClassId, ClassInfoOf<T>>;
+
+	/// Store token info.
+	///
+	/// Returns `None` if token info not set or removed.
+	#[pallet::storage]
+	#[pallet::getter(fn tokens)]
+	pub type Tokens<T: Config> =
+		StorageDoubleMap<_, Twox64Concat, T::ClassId, Twox64Concat, T::TokenId, TokenInfoOf<T>>;
+
+	/// Token existence check by owner and class ID.
+	// TODO: pallet macro doesn't support conditional compiling. Always having `TokensByOwner` storage doesn't hurt but
+	// it could be removed once conditional compiling supported.
+	// #[cfg(not(feature = "disable-tokens-by-owner"))]
+	#[pallet::storage]
+	#[pallet::getter(fn tokens_by_owner)]
+	pub type TokensByOwner<T: Config> =
+		StorageDoubleMap<_, Twox64Concat, T::AccountId, Twox64Concat, (T::ClassId, T::TokenId), (), ValueQuery>;
+
+	#[pallet::genesis_config]
+	pub struct GenesisConfig<T: Config> {
+		pub tokens: Vec<GenesisTokens<T>>,
+	}
+
+	#[cfg(feature = "std")]
+	impl<T: Config> Default for GenesisConfig<T> {
+		fn default() -> Self {
+			GenesisConfig { tokens: vec![] }
+		}
 	}
-	add_extra_genesis {
-		config(tokens): Vec<GenesisTokens<T>>;
 
-		build(|config: &GenesisConfig<T>| {
-			config.tokens.iter().for_each(|token_class| {
-				let class_id = Module::<T>::create_class(&token_class.0, token_class.1.to_vec(), token_class.2.clone())
+	#[pallet::genesis_build]
+	impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
+		fn build(&self) {
+			self.tokens.iter().for_each(|token_class| {
+				let class_id = Pallet::<T>::create_class(&token_class.0, token_class.1.to_vec(), token_class.2.clone())
 					.expect("Create class cannot fail while building genesis");
 				for (account_id, token_metadata, token_data) in &token_class.3 {
-					Module::<T>::mint(&account_id, class_id, token_metadata.to_vec(), token_data.clone())
+					Pallet::<T>::mint(&account_id, class_id, token_metadata.to_vec(), token_data.clone())
 						.expect("Token mint cannot fail during genesis");
 				}
 			})
-		})
+		}
 	}
-}
-
-decl_module! {
-	pub struct Module<T: Config> for enum Call where origin: T::Origin {
-	}
-}
-
-impl<T: Config> Module<T> {
-	/// Create NFT(non fungible token) class
-	pub fn create_class(
-		owner: &T::AccountId,
-		metadata: Vec<u8>,
-		data: T::ClassData,
-	) -> Result<T::ClassId, DispatchError> {
-		let class_id = NextClassId::<T>::try_mutate(|id| -> Result<T::ClassId, DispatchError> {
-			let current_id = *id;
-			*id = id.checked_add(&One::one()).ok_or(Error::<T>::NoAvailableClassId)?;
-			Ok(current_id)
-		})?;
-
-		let info = ClassInfo {
-			metadata,
-			total_issuance: Default::default(),
-			owner: owner.clone(),
-			data,
-		};
-		Classes::<T>::insert(class_id, info);
-
-		Ok(class_id)
-	}
-
-	/// Transfer NFT(non fungible token) from `from` account to `to` account
-	pub fn transfer(from: &T::AccountId, to: &T::AccountId, token: (T::ClassId, T::TokenId)) -> DispatchResult {
-		Tokens::<T>::try_mutate(token.0, token.1, |token_info| -> DispatchResult {
-			let mut info = token_info.as_mut().ok_or(Error::<T>::TokenNotFound)?;
-			ensure!(info.owner == *from, Error::<T>::NoPermission);
-			if from == to {
-				// no change needed
-				return Ok(());
-			}
-
-			info.owner = to.clone();
-
-			#[cfg(not(feature = "disable-tokens-by-owner"))]
-			{
-				TokensByOwner::<T>::remove(from, token);
-				TokensByOwner::<T>::insert(to, token, ());
-			}
 
-			Ok(())
-		})
-	}
-
-	/// Mint NFT(non fungible token) to `owner`
-	pub fn mint(
-		owner: &T::AccountId,
-		class_id: T::ClassId,
-		metadata: Vec<u8>,
-		data: T::TokenData,
-	) -> Result<T::TokenId, DispatchError> {
-		NextTokenId::<T>::try_mutate(class_id, |id| -> Result<T::TokenId, DispatchError> {
-			let token_id = *id;
-			*id = id.checked_add(&One::one()).ok_or(Error::<T>::NoAvailableTokenId)?;
-
-			Classes::<T>::try_mutate(class_id, |class_info| -> DispatchResult {
-				let info = class_info.as_mut().ok_or(Error::<T>::ClassNotFound)?;
-				info.total_issuance = info
-					.total_issuance
-					.checked_add(&One::one())
-					.ok_or(Error::<T>::NumOverflow)?;
-				Ok(())
+	#[pallet::hooks]
+	impl<T: Config> Hooks<T::BlockNumber> for Pallet<T> {}
+
+	#[pallet::call]
+	impl<T: Config> Pallet<T> {}
+
+	impl<T: Config> Pallet<T> {
+		/// Create NFT(non fungible token) class
+		pub fn create_class(
+			owner: &T::AccountId,
+			metadata: Vec<u8>,
+			data: T::ClassData,
+		) -> Result<T::ClassId, DispatchError> {
+			let class_id = NextClassId::<T>::try_mutate(|id| -> Result<T::ClassId, DispatchError> {
+				let current_id = *id;
+				*id = id.checked_add(&One::one()).ok_or(Error::<T>::NoAvailableClassId)?;
+				Ok(current_id)
 			})?;
 
-			let token_info = TokenInfo {
+			let info = ClassInfo {
 				metadata,
+				total_issuance: Default::default(),
 				owner: owner.clone(),
 				data,
 			};
-			Tokens::<T>::insert(class_id, token_id, token_info);
-			#[cfg(not(feature = "disable-tokens-by-owner"))]
-			TokensByOwner::<T>::insert(owner, (class_id, token_id), ());
+			Classes::<T>::insert(class_id, info);
+
+			Ok(class_id)
+		}
+
+		/// Transfer NFT(non fungible token) from `from` account to `to` account
+		pub fn transfer(from: &T::AccountId, to: &T::AccountId, token: (T::ClassId, T::TokenId)) -> DispatchResult {
+			Tokens::<T>::try_mutate(token.0, token.1, |token_info| -> DispatchResult {
+				let mut info = token_info.as_mut().ok_or(Error::<T>::TokenNotFound)?;
+				ensure!(info.owner == *from, Error::<T>::NoPermission);
+				if from == to {
+					// no change needed
+					return Ok(());
+				}
 
-			Ok(token_id)
-		})
-	}
+				info.owner = to.clone();
 
-	/// Burn NFT(non fungible token) from `owner`
-	pub fn burn(owner: &T::AccountId, token: (T::ClassId, T::TokenId)) -> DispatchResult {
-		Tokens::<T>::try_mutate_exists(token.0, token.1, |token_info| -> DispatchResult {
-			let t = token_info.take().ok_or(Error::<T>::TokenNotFound)?;
-			ensure!(t.owner == *owner, Error::<T>::NoPermission);
-
-			Classes::<T>::try_mutate(token.0, |class_info| -> DispatchResult {
-				let info = class_info.as_mut().ok_or(Error::<T>::ClassNotFound)?;
-				info.total_issuance = info
-					.total_issuance
-					.checked_sub(&One::one())
-					.ok_or(Error::<T>::NumOverflow)?;
-				Ok(())
-			})?;
+				#[cfg(not(feature = "disable-tokens-by-owner"))]
+				{
+					TokensByOwner::<T>::remove(from, token);
+					TokensByOwner::<T>::insert(to, token, ());
+				}
 
-			#[cfg(not(feature = "disable-tokens-by-owner"))]
-			TokensByOwner::<T>::remove(owner, token);
+				Ok(())
+			})
+		}
+
+		/// Mint NFT(non fungible token) to `owner`
+		pub fn mint(
+			owner: &T::AccountId,
+			class_id: T::ClassId,
+			metadata: Vec<u8>,
+			data: T::TokenData,
+		) -> Result<T::TokenId, DispatchError> {
+			NextTokenId::<T>::try_mutate(class_id, |id| -> Result<T::TokenId, DispatchError> {
+				let token_id = *id;
+				*id = id.checked_add(&One::one()).ok_or(Error::<T>::NoAvailableTokenId)?;
+
+				Classes::<T>::try_mutate(class_id, |class_info| -> DispatchResult {
+					let info = class_info.as_mut().ok_or(Error::<T>::ClassNotFound)?;
+					info.total_issuance = info
+						.total_issuance
+						.checked_add(&One::one())
+						.ok_or(Error::<T>::NumOverflow)?;
+					Ok(())
+				})?;
+
+				let token_info = TokenInfo {
+					metadata,
+					owner: owner.clone(),
+					data,
+				};
+				Tokens::<T>::insert(class_id, token_id, token_info);
+				#[cfg(not(feature = "disable-tokens-by-owner"))]
+				TokensByOwner::<T>::insert(owner, (class_id, token_id), ());
+
+				Ok(token_id)
+			})
+		}
+
+		/// Burn NFT(non fungible token) from `owner`
+		pub fn burn(owner: &T::AccountId, token: (T::ClassId, T::TokenId)) -> DispatchResult {
+			Tokens::<T>::try_mutate_exists(token.0, token.1, |token_info| -> DispatchResult {
+				let t = token_info.take().ok_or(Error::<T>::TokenNotFound)?;
+				ensure!(t.owner == *owner, Error::<T>::NoPermission);
+
+				Classes::<T>::try_mutate(token.0, |class_info| -> DispatchResult {
+					let info = class_info.as_mut().ok_or(Error::<T>::ClassNotFound)?;
+					info.total_issuance = info
+						.total_issuance
+						.checked_sub(&One::one())
+						.ok_or(Error::<T>::NumOverflow)?;
+					Ok(())
+				})?;
+
+				#[cfg(not(feature = "disable-tokens-by-owner"))]
+				TokensByOwner::<T>::remove(owner, token);
 
-			Ok(())
-		})
-	}
+				Ok(())
+			})
+		}
 
-	/// Destroy NFT(non fungible token) class
-	pub fn destroy_class(owner: &T::AccountId, class_id: T::ClassId) -> DispatchResult {
-		Classes::<T>::try_mutate_exists(class_id, |class_info| -> DispatchResult {
-			let info = class_info.take().ok_or(Error::<T>::ClassNotFound)?;
-			ensure!(info.owner == *owner, Error::<T>::NoPermission);
-			ensure!(info.total_issuance == Zero::zero(), Error::<T>::CannotDestroyClass);
+		/// Destroy NFT(non fungible token) class
+		pub fn destroy_class(owner: &T::AccountId, class_id: T::ClassId) -> DispatchResult {
+			Classes::<T>::try_mutate_exists(class_id, |class_info| -> DispatchResult {
+				let info = class_info.take().ok_or(Error::<T>::ClassNotFound)?;
+				ensure!(info.owner == *owner, Error::<T>::NoPermission);
+				ensure!(info.total_issuance == Zero::zero(), Error::<T>::CannotDestroyClass);
 
-			NextTokenId::<T>::remove(class_id);
+				NextTokenId::<T>::remove(class_id);
 
-			Ok(())
-		})
-	}
+				Ok(())
+			})
+		}
 
-	pub fn is_owner(account: &T::AccountId, token: (T::ClassId, T::TokenId)) -> bool {
-		#[cfg(feature = "disable-tokens-by-owner")]
-		return Tokens::<T>::get(token.0, token.1).map_or(false, |token| token.owner == *account);
+		pub fn is_owner(account: &T::AccountId, token: (T::ClassId, T::TokenId)) -> bool {
+			#[cfg(feature = "disable-tokens-by-owner")]
+			return Tokens::<T>::get(token.0, token.1).map_or(false, |token| token.owner == *account);
 
-		#[cfg(not(feature = "disable-tokens-by-owner"))]
-		TokensByOwner::<T>::contains_key(account, token)
+			#[cfg(not(feature = "disable-tokens-by-owner"))]
+			TokensByOwner::<T>::contains_key(account, token)
+		}
 	}
 }
diff --git a/oracle/src/default_combine_data.rs b/oracle/src/default_combine_data.rs
index 5c097e4..4f096cf 100644
--- a/oracle/src/default_combine_data.rs
+++ b/oracle/src/default_combine_data.rs
@@ -1,19 +1,17 @@
-use crate::{Config, DefaultInstance, Instance, MomentOf, TimestampedValueOf};
+use crate::{Config, MomentOf, TimestampedValueOf};
 use frame_support::traits::{Get, Time};
 use orml_traits::CombineData;
 use sp_std::{marker, prelude::*};
 
 /// Sort by value and returns median timestamped value.
 /// Returns prev_value if not enough valid values.
-pub struct DefaultCombineData<T, MinimumCount, ExpiresIn, I = DefaultInstance>(
-	marker::PhantomData<(T, I, MinimumCount, ExpiresIn)>,
-);
+pub struct DefaultCombineData<T, MinimumCount, ExpiresIn, I = ()>(marker::PhantomData<(T, I, MinimumCount, ExpiresIn)>);
 
 impl<T, I, MinimumCount, ExpiresIn> CombineData<<T as Config<I>>::OracleKey, TimestampedValueOf<T, I>>
 	for DefaultCombineData<T, MinimumCount, ExpiresIn, I>
 where
 	T: Config<I>,
-	I: Instance,
+	I: 'static,
 	MinimumCount: Get<u32>,
 	ExpiresIn: Get<MomentOf<T, I>>,
 {
diff --git a/oracle/src/lib.rs b/oracle/src/lib.rs
index 6a34b75..58e6062 100644
--- a/oracle/src/lib.rs
+++ b/oracle/src/lib.rs
@@ -19,274 +19,312 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 // Disable the following two lints since they originate from an external macro (namely decl_storage)
 #![allow(clippy::string_lit_as_bytes)]
+#![allow(clippy::unused_unit)]
 
 mod default_combine_data;
 mod default_weight;
 mod mock;
 mod tests;
 
-pub trait WeightInfo {
-	fn feed_values(c: u32) -> Weight;
-	fn on_finalize() -> Weight;
-}
+pub use module::*;
 
-use codec::{Decode, Encode};
-pub use default_combine_data::DefaultCombineData;
-use frame_support::{
-	decl_error, decl_event, decl_module, decl_storage,
-	dispatch::DispatchResultWithPostInfo,
-	ensure,
-	traits::{ChangeMembers, Get, InitializeMembers, Time},
-	weights::{DispatchClass, Pays, Weight},
-	IterableStorageMap, Parameter,
-};
-use frame_system::{ensure_root, ensure_signed};
-pub use orml_traits::{CombineData, DataFeeder, DataProvider, DataProviderExtended, OnNewData};
-use orml_utilities::OrderedSet;
-#[cfg(feature = "std")]
-use serde::{Deserialize, Serialize};
-use sp_runtime::{traits::Member, DispatchResult, RuntimeDebug};
-use sp_std::{prelude::*, vec};
-
-type MomentOf<T, I = DefaultInstance> = <<T as Config<I>>::Time as Time>::Moment;
-pub type TimestampedValueOf<T, I = DefaultInstance> = TimestampedValue<<T as Config<I>>::OracleValue, MomentOf<T, I>>;
-
-#[derive(Encode, Decode, RuntimeDebug, Eq, PartialEq, Clone, Copy, Ord, PartialOrd)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-pub struct TimestampedValue<Value, Moment> {
-	pub value: Value,
-	pub timestamp: Moment,
-}
+#[frame_support::pallet]
+pub mod module {
+	use codec::{Decode, Encode};
+
+	use frame_support::{
+		ensure,
+		pallet_prelude::*,
+		traits::{ChangeMembers, Get, InitializeMembers, Time},
+		weights::{Pays, Weight},
+		Parameter,
+	};
+	use frame_system::{ensure_root, ensure_signed, pallet_prelude::*};
+	pub use orml_traits::{CombineData, DataFeeder, DataProvider, DataProviderExtended, OnNewData};
+	use orml_utilities::OrderedSet;
+
+	#[cfg(feature = "std")]
+	use serde::{Deserialize, Serialize};
 
-pub trait Config<I: Instance = DefaultInstance>: frame_system::Config {
-	type Event: From<Event<Self, I>> + Into<<Self as frame_system::Config>::Event>;
+	use sp_runtime::{traits::Member, DispatchResult, RuntimeDebug};
+	use sp_std::{prelude::*, vec};
 
-	/// Hook on new data received
-	type OnNewData: OnNewData<Self::AccountId, Self::OracleKey, Self::OracleValue>;
+	pub use crate::default_combine_data::DefaultCombineData;
 
-	/// Provide the implementation to combine raw values to produce aggregated
-	/// value
-	type CombineData: CombineData<Self::OracleKey, TimestampedValueOf<Self, I>>;
+	pub trait WeightInfo {
+		fn feed_values(c: u32) -> Weight;
+		fn on_finalize() -> Weight;
+	}
+
+	pub type MomentOf<T, I = ()> = <<T as Config<I>>::Time as Time>::Moment;
+	pub type TimestampedValueOf<T, I = ()> = TimestampedValue<<T as Config<I>>::OracleValue, MomentOf<T, I>>;
 
-	/// Time provider
-	type Time: Time;
+	#[derive(Encode, Decode, RuntimeDebug, Eq, PartialEq, Clone, Copy, Ord, PartialOrd)]
+	#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+	pub struct TimestampedValue<Value, Moment> {
+		pub value: Value,
+		pub timestamp: Moment,
+	}
 
-	/// The data key type
-	type OracleKey: Parameter + Member;
+	#[pallet::config]
+	pub trait Config<I: 'static = ()>: frame_system::Config {
+		type Event: From<Event<Self, I>> + IsType<<Self as frame_system::Config>::Event>;
 
-	/// The data value type
-	type OracleValue: Parameter + Member + Ord;
+		/// Hook on new data received
+		type OnNewData: OnNewData<Self::AccountId, Self::OracleKey, Self::OracleValue>;
 
-	/// The root operator account id, recorad all sudo feeds on this account.
-	type RootOperatorAccountId: Get<Self::AccountId>;
+		/// Provide the implementation to combine raw values to produce
+		/// aggregated value
+		type CombineData: CombineData<Self::OracleKey, TimestampedValueOf<Self, I>>;
 
-	/// Weight information for extrinsics in this module.
-	type WeightInfo: WeightInfo;
-}
+		/// Time provider
+		type Time: Time;
 
-decl_error! {
-	pub enum Error for Module<T: Config<I>, I: Instance> {
+		/// The data key type
+		type OracleKey: Parameter + Member;
+
+		/// The data value type
+		type OracleValue: Parameter + Member + Ord;
+
+		/// The root operator account id, record all sudo feeds on this account.
+		type RootOperatorAccountId: Get<Self::AccountId>;
+
+		/// Weight information for extrinsics in this module.
+		type WeightInfo: WeightInfo;
+	}
+
+	#[pallet::pallet]
+	pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
+
+	#[pallet::error]
+	pub enum Error<T, I = ()> {
 		/// Sender does not have permission
 		NoPermission,
 		/// Feeder has already feeded at this block
 		AlreadyFeeded,
 	}
-}
 
-decl_event!(
-	pub enum Event<T, I=DefaultInstance> where
-		<T as frame_system::Config>::AccountId,
-		<T as Config<I>>::OracleKey,
-		<T as Config<I>>::OracleValue,
-	{
+	#[pallet::event]
+	#[pallet::generate_deposit(fn deposit_event)]
+	pub enum Event<T: Config<I>, I: 'static = ()> {
 		/// New feed data is submitted. [sender, values]
-		NewFeedData(AccountId, Vec<(OracleKey, OracleValue)>),
+		NewFeedData(T::AccountId, Vec<(T::OracleKey, T::OracleValue)>),
 	}
-);
-
-decl_storage! {
-	trait Store for Module<T: Config<I>, I: Instance=DefaultInstance> as Oracle {
 
-		/// Raw values for each oracle operators
-		pub RawValues get(fn raw_values): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::OracleKey => Option<TimestampedValueOf<T, I>>;
-
-		/// True if Self::values(key) is up to date, otherwise the value is stale
-		pub IsUpdated get(fn is_updated): map hasher(twox_64_concat) <T as Config<I>>::OracleKey => bool;
-
-		/// Combined value, may not be up to date
-		pub Values get(fn values): map hasher(twox_64_concat) <T as Config<I>>::OracleKey => Option<TimestampedValueOf<T, I>>;
-
-		/// If an oracle operator has feed a value in this block
-		HasDispatched: OrderedSet<T::AccountId>;
-
-		// TODO: this shouldn't be required https://github.com/paritytech/substrate/issues/6041
-		/// The current members of the collective. This is stored sorted (just by value).
-		pub Members get(fn members) config(): OrderedSet<T::AccountId>;
+	/// Raw values for each oracle operators
+	#[pallet::storage]
+	#[pallet::getter(fn raw_values)]
+	pub type RawValues<T: Config<I>, I: 'static = ()> =
+		StorageDoubleMap<_, Twox64Concat, T::AccountId, Twox64Concat, T::OracleKey, TimestampedValueOf<T, I>>;
+
+	/// True if Self::values(key) is up to date, otherwise the value is stale
+	#[pallet::storage]
+	#[pallet::getter(fn is_updated)]
+	pub type IsUpdated<T: Config<I>, I: 'static = ()> =
+		StorageMap<_, Twox64Concat, <T as Config<I>>::OracleKey, bool, ValueQuery>;
+
+	/// Combined value, may not be up to date
+	#[pallet::storage]
+	#[pallet::getter(fn values)]
+	pub type Values<T: Config<I>, I: 'static = ()> =
+		StorageMap<_, Twox64Concat, <T as Config<I>>::OracleKey, TimestampedValueOf<T, I>>;
+
+	/// If an oracle operator has feed a value in this block
+	#[pallet::storage]
+	type HasDispatched<T: Config<I>, I: 'static = ()> = StorageValue<_, OrderedSet<T::AccountId>, ValueQuery>;
+
+	// TODO: this shouldn't be required https://github.com/paritytech/substrate/issues/6041
+	/// The current members of the collective. This is stored sorted (just by
+	/// value).
+	#[pallet::storage]
+	#[pallet::getter(fn members)]
+	pub type Members<T: Config<I>, I: 'static = ()> = StorageValue<_, OrderedSet<T::AccountId>, ValueQuery>;
+
+	#[pallet::storage]
+	#[pallet::getter(fn nonces)]
+	pub type Nonces<T: Config<I>, I: 'static = ()> = StorageMap<_, Twox64Concat, T::AccountId, u32>;
+
+	#[pallet::genesis_config]
+	pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
+		pub members: OrderedSet<T::AccountId>,
+		pub phantom: sp_std::marker::PhantomData<I>,
+	}
 
-		pub Nonces get(fn nonces): map hasher(twox_64_concat) T::AccountId => u32;
+	#[cfg(feature = "std")]
+	impl<T: Config<I>, I: 'static> Default for GenesisConfig<T, I> {
+		fn default() -> Self {
+			GenesisConfig {
+				members: Default::default(),
+				phantom: Default::default(),
+			}
+		}
 	}
 
-	add_extra_genesis {
-		config(phantom): sp_std::marker::PhantomData<I>;
+	#[pallet::genesis_build]
+	impl<T: Config<I>, I: 'static> GenesisBuild<T, I> for GenesisConfig<T, I> {
+		fn build(&self) {
+			<Members<T, I>>::put(self.members.clone());
+		}
 	}
-}
 
-decl_module! {
-	pub struct Module<T: Config<I>, I: Instance=DefaultInstance> for enum Call where origin: T::Origin {
-		type Error = Error<T, I>;
+	#[pallet::hooks]
+	impl<T: Config<I>, I: 'static> Hooks<T::BlockNumber> for Pallet<T, I> {
+		/// `on_initialize` to return the weight used in `on_finalize`.
+		fn on_initialize(_n: T::BlockNumber) -> Weight {
+			T::WeightInfo::on_finalize()
+		}
 
-		fn deposit_event() = default;
+		fn on_finalize(_n: T::BlockNumber) {
+			// cleanup for next block
+			<HasDispatched<T, I>>::kill();
+		}
+	}
 
+	#[pallet::call]
+	impl<T: Config<I>, I: 'static> Pallet<T, I> {
 		/// Feed the external value.
 		///
 		/// Require authorized operator.
-		#[weight = (T::WeightInfo::feed_values(values.len() as u32), DispatchClass::Operational)]
+		#[pallet::weight(T::WeightInfo::feed_values(values.len() as u32))]
 		pub fn feed_values(
-			origin,
+			origin: OriginFor<T>,
 			values: Vec<(T::OracleKey, T::OracleValue)>,
 		) -> DispatchResultWithPostInfo {
-			let feeder = ensure_signed(origin.clone()).or_else(|_| ensure_root(origin).map(|_| T::RootOperatorAccountId::get()))?;
+			let feeder = ensure_signed(origin.clone())
+				.or_else(|_| ensure_root(origin).map(|_| T::RootOperatorAccountId::get()))?;
 			Self::do_feed_values(feeder, values)?;
 			Ok(Pays::No.into())
 		}
+	}
 
-		/// `on_initialize` to return the weight used in `on_finalize`.
-		fn on_initialize() -> Weight {
-			T::WeightInfo::on_finalize()
+	impl<T: Config<I>, I: 'static> Pallet<T, I> {
+		pub fn read_raw_values(key: &T::OracleKey) -> Vec<TimestampedValueOf<T, I>> {
+			Self::members()
+				.0
+				.iter()
+				.chain(vec![T::RootOperatorAccountId::get()].iter())
+				.filter_map(|x| Self::raw_values(x, key))
+				.collect()
 		}
 
-		fn on_finalize(_n: T::BlockNumber) {
-			// cleanup for next block
-			<HasDispatched<T, I>>::kill();
+		/// Returns fresh combined value if has update, or latest combined
+		/// value.
+		///
+		/// Note this will update values storage if has update.
+		pub fn get(key: &T::OracleKey) -> Option<TimestampedValueOf<T, I>> {
+			if Self::is_updated(key) {
+				<Values<T, I>>::get(key)
+			} else {
+				let timestamped = Self::combined(key)?;
+				<Values<T, I>>::insert(key, timestamped.clone());
+				IsUpdated::<T, I>::insert(key, true);
+				Some(timestamped)
+			}
 		}
-	}
-}
-
-impl<T: Config<I>, I: Instance> Module<T, I> {
-	pub fn read_raw_values(key: &T::OracleKey) -> Vec<TimestampedValueOf<T, I>> {
-		Self::members()
-			.0
-			.iter()
-			.chain(vec![T::RootOperatorAccountId::get()].iter())
-			.filter_map(|x| Self::raw_values(x, key))
-			.collect()
-	}
 
-	/// Returns fresh combined value if has update, or latest combined value.
-	///
-	/// Note this will update values storage if has update.
-	pub fn get(key: &T::OracleKey) -> Option<TimestampedValueOf<T, I>> {
-		if Self::is_updated(key) {
-			<Values<T, I>>::get(key)
-		} else {
-			let timestamped = Self::combined(key)?;
-			<Values<T, I>>::insert(key, timestamped.clone());
-			IsUpdated::<T, I>::insert(key, true);
-			Some(timestamped)
+		/// Returns fresh combined value if has update, or latest combined
+		/// value.
+		///
+		/// This is a no-op function which would not change storage.
+		pub fn get_no_op(key: &T::OracleKey) -> Option<TimestampedValueOf<T, I>> {
+			if Self::is_updated(key) {
+				Self::values(key)
+			} else {
+				Self::combined(key)
+			}
 		}
-	}
 
-	/// Returns fresh combined value if has update, or latest combined value.
-	///
-	/// This is a no-op function which would not change storage.
-	pub fn get_no_op(key: &T::OracleKey) -> Option<TimestampedValueOf<T, I>> {
-		if Self::is_updated(key) {
-			Self::values(key)
-		} else {
-			Self::combined(key)
+		#[allow(clippy::complexity)]
+		pub fn get_all_values() -> Vec<(T::OracleKey, Option<TimestampedValueOf<T, I>>)> {
+			<Values<T, I>>::iter()
+				.map(|(key, _)| key)
+				.map(|key| {
+					let v = Self::get_no_op(&key);
+					(key, v)
+				})
+				.collect()
 		}
-	}
-
-	#[allow(clippy::complexity)]
-	pub fn get_all_values() -> Vec<(T::OracleKey, Option<TimestampedValueOf<T, I>>)> {
-		<Values<T, I>>::iter()
-			.map(|(key, _)| key)
-			.map(|key| {
-				let v = Self::get_no_op(&key);
-				(key, v)
-			})
-			.collect()
-	}
 
-	fn combined(key: &T::OracleKey) -> Option<TimestampedValueOf<T, I>> {
-		let values = Self::read_raw_values(key);
-		T::CombineData::combine_data(key, values, Self::values(key))
-	}
+		fn combined(key: &T::OracleKey) -> Option<TimestampedValueOf<T, I>> {
+			let values = Self::read_raw_values(key);
+			T::CombineData::combine_data(key, values, Self::values(key))
+		}
 
-	fn do_feed_values(who: T::AccountId, values: Vec<(T::OracleKey, T::OracleValue)>) -> DispatchResult {
-		// ensure feeder is authorized
-		ensure!(
-			Self::members().contains(&who) || who == T::RootOperatorAccountId::get(),
-			Error::<T, I>::NoPermission
-		);
-
-		// ensure account hasn't dispatched an updated yet
-		ensure!(
-			HasDispatched::<T, I>::mutate(|set| set.insert(who.clone())),
-			Error::<T, I>::AlreadyFeeded
-		);
-
-		let now = T::Time::now();
-		for (key, value) in &values {
-			let timestamped = TimestampedValue {
-				value: value.clone(),
-				timestamp: now,
-			};
-			RawValues::<T, I>::insert(&who, &key, timestamped);
-			IsUpdated::<T, I>::remove(&key);
-
-			T::OnNewData::on_new_data(&who, &key, &value);
+		fn do_feed_values(who: T::AccountId, values: Vec<(T::OracleKey, T::OracleValue)>) -> DispatchResult {
+			// ensure feeder is authorized
+			ensure!(
+				Self::members().contains(&who) || who == T::RootOperatorAccountId::get(),
+				Error::<T, I>::NoPermission
+			);
+
+			// ensure account hasn't dispatched an updated yet
+			ensure!(
+				HasDispatched::<T, I>::mutate(|set| set.insert(who.clone())),
+				Error::<T, I>::AlreadyFeeded
+			);
+
+			let now = T::Time::now();
+			for (key, value) in &values {
+				let timestamped = TimestampedValue {
+					value: value.clone(),
+					timestamp: now,
+				};
+				RawValues::<T, I>::insert(&who, &key, timestamped);
+				IsUpdated::<T, I>::remove(&key);
+
+				T::OnNewData::on_new_data(&who, &key, &value);
+			}
+			Self::deposit_event(Event::NewFeedData(who, values));
+			Ok(())
 		}
-		Self::deposit_event(RawEvent::NewFeedData(who, values));
-		Ok(())
 	}
-}
 
-impl<T: Config<I>, I: Instance> InitializeMembers<T::AccountId> for Module<T, I> {
-	fn initialize_members(members: &[T::AccountId]) {
-		if !members.is_empty() {
-			assert!(Members::<T, I>::get().0.is_empty(), "Members are already initialized!");
-			Members::<T, I>::put(OrderedSet::from_sorted_set(members.into()));
+	impl<T: Config<I>, I: 'static> InitializeMembers<T::AccountId> for Pallet<T, I> {
+		fn initialize_members(members: &[T::AccountId]) {
+			if !members.is_empty() {
+				assert!(Members::<T, I>::get().0.is_empty(), "Members are already initialized!");
+				Members::<T, I>::put(OrderedSet::from_sorted_set(members.into()));
+			}
 		}
 	}
-}
 
-impl<T: Config<I>, I: Instance> ChangeMembers<T::AccountId> for Module<T, I> {
-	fn change_members_sorted(_incoming: &[T::AccountId], outgoing: &[T::AccountId], new: &[T::AccountId]) {
-		// remove session keys and its values
-		for removed in outgoing {
-			RawValues::<T, I>::remove_prefix(removed);
-		}
+	impl<T: Config<I>, I: 'static> ChangeMembers<T::AccountId> for Pallet<T, I> {
+		fn change_members_sorted(_incoming: &[T::AccountId], outgoing: &[T::AccountId], new: &[T::AccountId]) {
+			// remove session keys and its values
+			for removed in outgoing {
+				RawValues::<T, I>::remove_prefix(removed);
+			}
 
-		Members::<T, I>::put(OrderedSet::from_sorted_set(new.into()));
+			Members::<T, I>::put(OrderedSet::from_sorted_set(new.into()));
 
-		// not bothering to track which key needs recompute, just update all
-		IsUpdated::<T, I>::remove_all();
-	}
+			// not bothering to track which key needs recompute, just update all
+			IsUpdated::<T, I>::remove_all();
+		}
 
-	fn set_prime(_prime: Option<T::AccountId>) {
-		// nothing
+		fn set_prime(_prime: Option<T::AccountId>) {
+			// nothing
+		}
 	}
-}
 
-impl<T: Config<I>, I: Instance> DataProvider<T::OracleKey, T::OracleValue> for Module<T, I> {
-	fn get(key: &T::OracleKey) -> Option<T::OracleValue> {
-		Self::get(key).map(|timestamped_value| timestamped_value.value)
-	}
-}
-impl<T: Config<I>, I: Instance> DataProviderExtended<T::OracleKey, TimestampedValueOf<T, I>> for Module<T, I> {
-	fn get_no_op(key: &T::OracleKey) -> Option<TimestampedValueOf<T, I>> {
-		Self::get_no_op(key)
+	impl<T: Config<I>, I: 'static> DataProvider<T::OracleKey, T::OracleValue> for Pallet<T, I> {
+		fn get(key: &T::OracleKey) -> Option<T::OracleValue> {
+			Self::get(key).map(|timestamped_value| timestamped_value.value)
+		}
 	}
-	#[allow(clippy::complexity)]
-	fn get_all_values() -> Vec<(T::OracleKey, Option<TimestampedValueOf<T, I>>)> {
-		Self::get_all_values()
+	impl<T: Config<I>, I: 'static> DataProviderExtended<T::OracleKey, TimestampedValueOf<T, I>> for Pallet<T, I> {
+		fn get_no_op(key: &T::OracleKey) -> Option<TimestampedValueOf<T, I>> {
+			Self::get_no_op(key)
+		}
+		#[allow(clippy::complexity)]
+		fn get_all_values() -> Vec<(T::OracleKey, Option<TimestampedValueOf<T, I>>)> {
+			Self::get_all_values()
+		}
 	}
-}
 
-impl<T: Config<I>, I: Instance> DataFeeder<T::OracleKey, T::OracleValue, T::AccountId> for Module<T, I> {
-	fn feed_value(who: T::AccountId, key: T::OracleKey, value: T::OracleValue) -> DispatchResult {
-		Self::do_feed_values(who, vec![(key, value)])?;
-		Ok(())
+	impl<T: Config<I>, I: 'static> DataFeeder<T::OracleKey, T::OracleValue, T::AccountId> for Pallet<T, I> {
+		fn feed_value(who: T::AccountId, key: T::OracleKey, value: T::OracleValue) -> DispatchResult {
+			Self::do_feed_values(who, vec![(key, value)])?;
+			Ok(())
+		}
 	}
 }
diff --git a/oracle/src/mock.rs b/oracle/src/mock.rs
index a35cbb8..d9fc427 100644
--- a/oracle/src/mock.rs
+++ b/oracle/src/mock.rs
@@ -2,7 +2,10 @@
 
 use super::*;
 
-use frame_support::{impl_outer_dispatch, impl_outer_event, impl_outer_origin, parameter_types};
+use frame_support::{
+	impl_outer_dispatch, impl_outer_event, impl_outer_origin, parameter_types,
+	traits::{GenesisBuild, Time},
+};
 use sp_core::H256;
 use sp_runtime::{
 	testing::Header,
diff --git a/oracle/src/tests.rs b/oracle/src/tests.rs
index 270203c..adbdf93 100644
--- a/oracle/src/tests.rs
+++ b/oracle/src/tests.rs
@@ -1,14 +1,12 @@
 #![cfg(test)]
 
-use crate::{
-	mock::{new_test_ext, AccountId, ModuleOracle, Origin, RootOperatorAccountId, System, Test, TestEvent, Timestamp},
-	Error, RawEvent, TimestampedValue,
-};
+use super::*;
 use frame_support::{
 	assert_noop, assert_ok,
 	traits::{ChangeMembers, OnFinalize},
 	weights::Pays,
 };
+use mock::*;
 
 #[test]
 fn should_feed_values_from_member() {
@@ -28,7 +26,7 @@ fn should_feed_values_from_member() {
 			Pays::No
 		);
 
-		let new_feed_data_event = TestEvent::oracle(RawEvent::NewFeedData(1, vec![(50, 1000), (51, 900), (52, 800)]));
+		let new_feed_data_event = TestEvent::oracle(Event::NewFeedData(1, vec![(50, 1000), (51, 900), (52, 800)]));
 		assert!(System::events()
 			.iter()
 			.any(|record| record.event == new_feed_data_event));
diff --git a/utilities/src/ordered_set.rs b/utilities/src/ordered_set.rs
index e5143c0..86e9d38 100644
--- a/utilities/src/ordered_set.rs
+++ b/utilities/src/ordered_set.rs
@@ -6,7 +6,7 @@ use sp_std::prelude::*;
 
 /// An ordered set backed by `Vec`
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(RuntimeDebug, PartialEq, Eq, Encode, Decode, Default)]
+#[derive(RuntimeDebug, PartialEq, Eq, Encode, Decode, Default, Clone)]
 pub struct OrderedSet<T>(pub Vec<T>);
 
 impl<T: Ord> OrderedSet<T> {
-- 
GitLab