From 33329a2be193eb1bf82c2e5bb2cfe6259cda5ba9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=82=B1=E6=98=8A?= <qiuhao951@gmail.com>
Date: Wed, 13 Nov 2019 08:18:06 +0800
Subject: [PATCH] feat: prices module (#23)

* feat: prices module

* fix: add check to avoid dividing by zero

* fix: delete num_traits dependency
---
 Cargo.dev.toml      |  1 +
 prices/Cargo.toml   | 32 +++++++++++++++++
 prices/src/lib.rs   | 37 ++++++++++++++++++++
 prices/src/mock.rs  | 83 +++++++++++++++++++++++++++++++++++++++++++++
 prices/src/tests.rs | 30 ++++++++++++++++
 5 files changed, 183 insertions(+)
 create mode 100644 prices/Cargo.toml
 create mode 100644 prices/src/lib.rs
 create mode 100644 prices/src/mock.rs
 create mode 100644 prices/src/tests.rs

diff --git a/Cargo.dev.toml b/Cargo.dev.toml
index d3408d2..cbb1ce1 100644
--- a/Cargo.dev.toml
+++ b/Cargo.dev.toml
@@ -3,6 +3,7 @@ members = [
 	"oracle",
 	"tokens",
 	"traits",
+	"prices",
 	"utilities",
 	"auction",
 ]
diff --git a/prices/Cargo.toml b/prices/Cargo.toml
new file mode 100644
index 0000000..b8ae725
--- /dev/null
+++ b/prices/Cargo.toml
@@ -0,0 +1,32 @@
+[package]
+name = "orml-prices"
+version = "0.1.0"
+authors = ["Acala Developers"]
+edition = "2018"
+
+[dependencies]
+serde = { version = "1.0", optional = true }
+codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false }
+sr-primitives = { git = "https://github.com/paritytech/substrate.git", default-features = false }
+srml-support = { package = "srml-support", git = "https://github.com/paritytech/substrate.git", default-features = false }
+srml-system = { package = "srml-system", git = "https://github.com/paritytech/substrate.git", default-features = false }
+runtime_io = { package = "sr-io", git = "https://github.com/paritytech/substrate.git", default-features = false }
+
+traits = { package = "orml-traits", path = "../traits", default-features = false }
+utilities = { package = "orml-utilities", path = "../utilities", default-features = false }
+
+[dev-dependencies]
+primitives = { package = "substrate-primitives",  git = "https://github.com/paritytech/substrate.git", default-features = false }
+
+[features]
+default = ["std"]
+std = [
+	"serde",
+	"codec/std",
+	"sr-primitives/std",
+	"srml-support/std",
+	"srml-system/std",
+	"runtime_io/std",
+	"traits/std",
+	"utilities/std",
+]
diff --git a/prices/src/lib.rs b/prices/src/lib.rs
new file mode 100644
index 0000000..645cafd
--- /dev/null
+++ b/prices/src/lib.rs
@@ -0,0 +1,37 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+use sr_primitives::traits::{MaybeSerializeDeserialize, Member, SimpleArithmetic, Zero};
+use srml_support::{decl_module, decl_storage, Parameter};
+use srml_system as system;
+use traits::{DataProvider, PriceProvider};
+
+pub trait Trait: system::Trait {
+	type CurrencyId: Parameter + Member + Default + Copy + MaybeSerializeDeserialize;
+	type Price: Parameter + Member + Zero + SimpleArithmetic + Copy + Ord;
+	type Source: DataProvider<Self::CurrencyId, Self::Price>;
+}
+
+mod mock;
+mod tests;
+
+decl_storage! {
+	trait Store for Module<T: Trait> as Prices { }
+}
+
+decl_module! {
+	pub struct Module<T: Trait> for enum Call where origin: T::Origin { }
+}
+
+impl<T: Trait> Module<T> {}
+
+impl<T: Trait> PriceProvider<T::CurrencyId, T::Price> for Module<T> {
+	fn get_price(base: T::CurrencyId, quote: T::CurrencyId) -> Option<T::Price> {
+		if let (Some(base_price), Some(quote_price)) = (T::Source::get(&base), (T::Source::get(&quote))) {
+			if !base_price.is_zero() {
+				return Some(quote_price / base_price);
+			}
+		}
+
+		None
+	}
+}
diff --git a/prices/src/mock.rs b/prices/src/mock.rs
new file mode 100644
index 0000000..5e86008
--- /dev/null
+++ b/prices/src/mock.rs
@@ -0,0 +1,83 @@
+//! Mocks for the prices module.
+
+#![cfg(test)]
+
+use primitives::H256;
+use sr_primitives::{testing::Header, traits::IdentityLookup, Perbill};
+use srml_support::{impl_outer_origin, parameter_types};
+
+use super::*;
+
+impl_outer_origin! {
+	pub enum Origin for Runtime {}
+}
+
+// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct Runtime;
+parameter_types! {
+	pub const BlockHashCount: u64 = 250;
+	pub const MaximumBlockWeight: u32 = 1024;
+	pub const MaximumBlockLength: u32 = 2 * 1024;
+	pub const AvailableBlockRatio: Perbill = Perbill::one();
+}
+
+pub type AccountId = u64;
+pub type BlockNumber = u64;
+
+impl system::Trait for Runtime {
+	type Origin = Origin;
+	type Index = u64;
+	type BlockNumber = BlockNumber;
+	type Call = ();
+	type Hash = H256;
+	type Hashing = ::sr_primitives::traits::BlakeTwo256;
+	type AccountId = AccountId;
+	type Lookup = IdentityLookup<Self::AccountId>;
+	type Header = Header;
+	type Event = ();
+	type BlockHashCount = BlockHashCount;
+	type MaximumBlockWeight = MaximumBlockWeight;
+	type MaximumBlockLength = MaximumBlockLength;
+	type AvailableBlockRatio = AvailableBlockRatio;
+	type Version = ();
+}
+
+type Price = u32;
+type CurrencyId = u32;
+
+pub struct MockDataProvider;
+impl DataProvider<CurrencyId, Price> for MockDataProvider {
+	fn get(currency: &CurrencyId) -> Option<Price> {
+		match currency {
+			0 => Some(0),
+			1 => Some(1),
+			2 => Some(2),
+			_ => None,
+		}
+	}
+}
+
+impl Trait for Runtime {
+	type CurrencyId = CurrencyId;
+	type Price = Price;
+	type Source = MockDataProvider;
+}
+
+pub type PricesModule = Module<Runtime>;
+
+pub struct ExtBuilder;
+
+impl Default for ExtBuilder {
+	fn default() -> Self {
+		ExtBuilder
+	}
+}
+
+impl ExtBuilder {
+	pub fn build(self) -> runtime_io::TestExternalities {
+		let t = system::GenesisConfig::default().build_storage::<Runtime>().unwrap();
+
+		t.into()
+	}
+}
diff --git a/prices/src/tests.rs b/prices/src/tests.rs
new file mode 100644
index 0000000..ac07fc9
--- /dev/null
+++ b/prices/src/tests.rs
@@ -0,0 +1,30 @@
+//! Unit tests for the prices module.
+
+#![cfg(test)]
+
+use super::*;
+use mock::{ExtBuilder, PricesModule};
+
+#[test]
+fn get_price_should_work() {
+	ExtBuilder::default().build().execute_with(|| {
+		assert_eq!(PricesModule::get_price(1, 2), Some(2));
+	});
+}
+
+#[test]
+fn price_is_none_should_not_panic() {
+	ExtBuilder::default().build().execute_with(|| {
+		assert_eq!(PricesModule::get_price(3, 3), None);
+		assert_eq!(PricesModule::get_price(3, 1), None);
+		assert_eq!(PricesModule::get_price(1, 3), None);
+	});
+}
+
+#[test]
+fn price_is_zero_should_not_panic() {
+	ExtBuilder::default().build().execute_with(|| {
+		assert_eq!(PricesModule::get_price(0, 0), None);
+		assert_eq!(PricesModule::get_price(1, 0), Some(0));
+	});
+}
-- 
GitLab