Skip to content

Commit

Permalink
Add orderbook order creation extrinsic
Browse files Browse the repository at this point in the history
  • Loading branch information
thea-leake committed Jul 12, 2023
1 parent 615499d commit 2c79f05
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 80 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pallets/order-book/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ sp-std = { git = "https://github.com/paritytech/substrate", default-features = f
orml-traits = { git = "https://github.com/open-web3-stack/open-runtime-module-library", default-features = false, branch = "polkadot-v0.9.38" }

cfg-types = { path = "../../libs/types", default-features = false }
cfg-traits = { path = "../../libs/traits", default-features = false }

frame-benchmarking = { git = "https://github.com/paritytech/substrate", default-features = false, optional = true, branch = "polkadot-v0.9.38" }

Expand Down
210 changes: 134 additions & 76 deletions pallets/order-book/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@ pub mod pallet {

use core::fmt::Debug;

use cfg_traits::fees::Fees;
use cfg_types::tokens::{CustomMetadata, GeneralCurrencyIndex};
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{
pallet_prelude::{DispatchResult, Member, OptionQuery, StorageDoubleMap, StorageNMap, *},
pallet_prelude::{
DispatchResult, Member, OptionQuery, StorageDoubleMap, StorageNMap, StorageValue, *,
},
traits::{tokens::AssetId, Currency, ReservableCurrency},
Twox64Concat,
};
Expand All @@ -42,26 +45,14 @@ pub mod pallet {
MultiCurrency, MultiReservableCurrency,
};
use scale_info::TypeInfo;
use sp_runtime::{
traits::{
fungibles::{self, Create, Destroy, Inspect, Mutate, Transfer},
tokens::{AssetId, Balance, BalanceConversion},
AtLeast32BitUnsigned, Hash,
},
DispatchResult,
};
use sp_runtime::traits::{AtLeast32BitUnsigned, EnsureAdd, EnsureMul, EnsureSub, Hash, One};

use super::*;

pub type BalanceOf<T> =
<<T as pallet::Config>::Fungibles as fungibles::Inspect<AccountIdOf<T>>>::Balance;

/// An exchange tradeable asset
pub type TradeableAsset<T> = <T as Config>::Fungibles;
/// Exchange tradeable asset id
pub type TradeableAssetId<T> =
<<T as Config>::Fungibles as Inspect<<T as frame_system::Config>::AccountId>>::AssetId;

/// Balance type for the reserve/deposit made when creating an Allowance
pub type DepositBalanceOf<T> = <<T as Config>::ReserveCurrency as Currency<
<T as frame_system::Config>::AccountId,
>>::Balance;
/// The current storage version.
const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);

Expand All @@ -75,13 +66,13 @@ pub mod pallet {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;

type AssetRegistry: asset_registry::Inspect<
AssetId = Self::CurrencyId,
Balance = BalanceOf<T>,
AssetId = Self::AssetCurrencyId,
Balance = Self::Balance,
CustomMetadata = CustomMetadata,
>;

/// Id type of Currency exchanges will take place for
type CurrencyId: AssetId
/// CurrencyId of Asset
type AssetCurrencyId: AssetId
+ Parameter
+ Debug
+ Default
Expand All @@ -92,44 +83,105 @@ pub mod pallet {
+ TypeInfo
+ MaxEncodedLen;

/// Id type for placed Orders
type OrderId: Parameter
+ Debug
+ Default
+ Member
+ Copy
+ MaybeSerializeDeserialize
+ Ord
+ TypeInfo
+ MaxEncodedLen;
/// Currency for Reserve/Unreserve with allowlist adding/removal,
/// given that the allowlist will be in storage
type ReserveCurrency: Currency<Self::AccountId> + ReservableCurrency<Self::AccountId>;

/// Type for placed Orders
type Nonce: Parameter
+ Debug
+ Default
/// Fee handler for the reserve/unreserve
/// Currently just stores the amounts, will be extended to handle
/// reserve/unreserve as well in future
type Fees: Fees<
AccountId = <Self as frame_system::Config>::AccountId,
Balance = DepositBalanceOf<Self>,
>;

/// Fee Key used to find amount for allowance reserve/unreserve
type OrdeFeeKey: Get<<Self::Fees as Fees>::FeeKey>;

type Balance: Parameter
+ Member
+ AtLeast32BitUnsigned
+ Default
+ Copy
+ EnsureAdd
+ EnsureSub
+ EnsureMul
+ MaybeSerializeDeserialize
+ Ord
+ TypeInfo
+ AtLeast32BitUnsigned
+ MaxEncodedLen;

type Balance: Parameter
type Nonce: Parameter
+ Member
+ AtLeast32BitUnsigned
+ Default
+ Copy
+ EnsureAdd
+ MaybeSerializeDeserialize
+ MaxEncodedLen;

/// Type for trade-able currency
type TradeableAsset: MultiReservableCurrency<
Self::AccountId,
Balance = <Self as pallet::Config>::Balance,
CurrencyId = Self::CurrencyId,
CurrencyId = Self::AssetCurrencyId,
>;
}
//
// Storage and storage types
//
#[derive(Clone, Copy, Debug, Encode, Decode, Eq, PartialEq, MaxEncodedLen, TypeInfo)]
pub struct Order<OrderId, AccountId, AssetId, Balance> {
order_id: OrderId,
placing_account: AccountId,
asset_in_id: AssetId,
asset_out_id: AssetId,
sell_amount: Balance,
price: Balance,
}

#[derive(Clone, Copy, Debug, Encode, Decode, Eq, PartialEq, MaxEncodedLen, TypeInfo)]
pub struct Claim<T: Config> {
claiming_account: T::AccountId,
order_claiming: T::Hash,
}

#[pallet::storage]
pub type Orders<T: Config> = StorageMap<
_,
Twox64Concat,
T::Hash,
Order<T::Hash, T::AccountId, T::AssetCurrencyId, T::Balance>,
OptionQuery,
>;

#[pallet::storage]
pub type UserOrders<T: Config> = StorageDoubleMap<
_,
Twox64Concat,
T::AccountId,
Twox64Concat,
T::Hash,
Order<T::Hash, T::AccountId, T::AssetCurrencyId, T::Balance>,
OptionQuery,
>;

/// Stores Nonce for orders placed
/// Given that Nonce is to ensure that all orders have a unique ID, we can
/// use just one Nonce, which means that we only have one val in storage,
/// and we don't have to insert new map values upon a new account/currency
/// order creation.
#[pallet::storage]
pub type NonceStore<T: Config> = StorageValue<_, T::Nonce, ValueQuery>;

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {}

#[pallet::error]
pub enum Error<T> {
InvalidAssetId,
ConflictingAssetIds,
InsufficientAssetFunds,
}

#[pallet::call]
impl<T: Config> Pallet<T> {
Expand All @@ -138,61 +190,67 @@ pub mod pallet {
#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(2, 2).ref_time())]
pub fn create_order(
origin: OriginFor<T>,
asset_in: AssetId,
asset_out: AssetId,
ammount: Balance,
price: Balance,
asset_in: T::AssetCurrencyId,
asset_out: T::AssetCurrencyId,
amount: T::Balance,
price: T::Balance,
) -> DispatchResult {
let account_id = ensure_signed(origin)?;
ensure!(asset_in != asset_out, Error::<T>::ConflictingAssetIds);
ensure!(
T::AssetRegistry::metadata(&asset_in).is_some(),
Error::<T>::InvalidAssetId
);
ensure!(
T::AssetRegistry::metadata(&asset_out).is_some(),
Error::<T>::InvalidAssetId
);
ensure!(
T::TradeableAsset::can_reserve(asset_in, &account_id, amount),
Error::<T>::InsufficientAssetFunds,
);
<NonceStore<T>>::try_mutate(|n| {
*n = n.ensure_add(T::Nonce::one())?;
Ok::<_, DispatchError>(())
})?;
let new_nonce = <NonceStore<T>>::get();
let order_id = Self::gen_hash(&account_id, asset_in, asset_out, new_nonce);
let new_order: Order<T::Hash> = Order {
order_id: order_id,
placing_account: account_id.clone(),
asset_in_id: asset_in,
asset_out_id: asset_out,
sell_amount: amount,
price: price,
};
T::TradeableAsset::reserve(asset_in, &account_id, amount)?;
<Orders<T>>::insert(order_id, new_order);
<UserOrders<T>>::insert(account_id, order_id, new_order);

Ok(())
}

#[pallet::call_index(1)]
// dummy weight for now
#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(2, 2).ref_time())]
pub fn cancel_order(origin: OriginFor<T>, order_id: OrderId) -> DispatchResult {
pub fn cancel_order(origin: OriginFor<T>, order_id: T::Hash) -> DispatchResult {
let account_id = ensure_signed(origin)?;
Ok(())
}

#[pallet::call_index(2)]
// dummy weight for now
#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(2, 2).ref_time())]
pub fn fill_order(origin: OriginFor<T>, order_id: OrderId) -> DispatchResult {
pub fn fill_order(origin: OriginFor<T>, order_id: T::Hash) -> DispatchResult {
Ok(())
}
}
//
// Storage and storage types
//
pub struct Order<AccountId, Balance, TradeableAsset> {
placing_account: AccountId,
asset_out_type: TradeableAsset,
asset_in_type: TradeableAsset,
price: Balance,
sell_amount: Balance,
}

pub struct Claim<AccountId, OrderId> {
claiming_account: AccountId,
order_claiming: OrderId,
}

/// Stores Nonce for orders placed
/// Given that Nonce is to ensure that all orders have a unique ID, we can
/// use just one Nonce, which means that we only have one val in storage,
/// and we don't have to insert new map values upon a new account/currency
/// order creation.
#[pallet::storage]
pub type NonceStore<T: Config> = StorageValue<_, T::Nonce, OptionQuery>;

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {}

impl<T: Config> Pallet<T> {
pub fn gen_hash(
placer: &T::AccountId,
asset_out: T::CurrencyId,
asset_in: T::CurrencyId,
asset_out: T::AssetCurrencyId,
asset_in: T::AssetCurrencyId,
nonce: T::Nonce,
) -> T::Hash {
(&placer, asset_in, asset_out, nonce).using_encoded(T::Hashing::hash)
Expand Down
24 changes: 20 additions & 4 deletions pallets/order-book/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ frame_support::construct_runtime!(
{
Balances: pallet_balances,
System: frame_system,
OrmlTokens: orml_tokens
OrderBook: order_book,
}
);
Expand Down Expand Up @@ -122,11 +123,26 @@ cfg_test_utils::mocks::orml_asset_registry::impl_mock_registry! {
CustomMetadata
}

impl orml_tokens::Config for Test {
type Amount = i64;
type Balance = Balance;
type CurrencyHooks = ();
type CurrencyId = CurrencyId;
type DustRemovalWhitelist = frame_support::traits::Nothing;
type ExistentialDeposits = ExistentialDeposits;
type MaxLocks = ();
type MaxReserves = ();
type ReserveIdentifier = [u8; 8];
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
}

impl order_book::Config for Runtime {
// type AssetRegistry = RegistryMock;
// type Balance = Balance;
// type CurrencyId = CurrencyId;
// type RuntimeEvent = RuntimeEvent;
type AssetCurrencyId = CurrencyId;
type AssetRegistry = RegistryMock;
type Balance = Balance;
type RuntimeEvent = RuntimeEvent;
type TradeableAsset = OrmlTokens;
}

pub fn new_test_ext() -> sp_io::TestExternalities {
Expand Down

0 comments on commit 2c79f05

Please sign in to comment.