Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/nft-auction/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ sp-runtime = { path = "../../substrate/primitives/runtime", default-features = f
frame-benchmarking = { path = "../../substrate/frame/benchmarking", default-features = false, optional = true }
frame-support = { path = "../../substrate/frame/support", default-features = false }
frame-system = { path = "../../substrate/frame/system", default-features = false }
log = { version = "0.4.14", default-features = false }

pallet-nft = { path = "../nft", default-features = false }

Expand Down
16 changes: 8 additions & 8 deletions pallets/nft-auction/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ benchmarks_instance_pallet! {
let auction_id = NFTAuction::<T, I>::current_auction_id();
let caller = owner.clone();
let expire = T::MinDeadline::get().saturating_mul(2u32.into());
}: _(SystemOrigin::Signed(caller.clone()), class, instance, get_dollars::<T, I>(20), get_dollars::<T, I>(80), expire)
}: _(SystemOrigin::Signed(caller.clone()), class, instance, get_dollars::<T, I>(20), get_dollars::<T, I>(80), expire, None)
verify {
assert_last_event::<T, I>(Event::<T, I>::CreatedDutchAuction(caller, auction_id).into());
}
Expand All @@ -67,7 +67,7 @@ benchmarks_instance_pallet! {
let (class, instance) = create_nft::<T, I>(&owner);
let auction_id = NFTAuction::<T, I>::current_auction_id();
let expire = T::MinDeadline::get().saturating_mul(2u32.into());
assert_ok!(NFTAuction::<T, I>::create_dutch(SystemOrigin::Signed(owner.clone()).into(), class, instance, get_dollars::<T, I>(20), get_dollars::<T, I>(80), expire));
assert_ok!(NFTAuction::<T, I>::create_dutch(SystemOrigin::Signed(owner.clone()).into(), class, instance, get_dollars::<T, I>(20), get_dollars::<T, I>(80), expire, None));

System::<T>::set_block_number(T::MinDeadline::get().saturating_add(1u32.into()));

Expand All @@ -87,7 +87,7 @@ benchmarks_instance_pallet! {
let (class, instance) = create_nft::<T, I>(&owner);
let auction_id = NFTAuction::<T, I>::current_auction_id();
let expire = T::MinDeadline::get().saturating_mul(2u32.into());
assert_ok!(NFTAuction::<T, I>::create_dutch(SystemOrigin::Signed(owner.clone()).into(), class, instance, get_dollars::<T, I>(20), get_dollars::<T, I>(80), expire));
assert_ok!(NFTAuction::<T, I>::create_dutch(SystemOrigin::Signed(owner.clone()).into(), class, instance, get_dollars::<T, I>(20), get_dollars::<T, I>(80), expire, None));

System::<T>::set_block_number(T::MinDeadline::get().saturating_add(1u32.into()));

Expand All @@ -111,7 +111,7 @@ benchmarks_instance_pallet! {
let (class, instance) = create_nft::<T, I>(&owner);
let auction_id = NFTAuction::<T, I>::current_auction_id();
let expire = T::MinDeadline::get().saturating_mul(2u32.into());
assert_ok!(NFTAuction::<T, I>::create_dutch(SystemOrigin::Signed(owner.clone()).into(), class, instance, get_dollars::<T, I>(20), get_dollars::<T, I>(80), expire));
assert_ok!(NFTAuction::<T, I>::create_dutch(SystemOrigin::Signed(owner.clone()).into(), class, instance, get_dollars::<T, I>(20), get_dollars::<T, I>(80), expire, None));

System::<T>::set_block_number(T::MinDeadline::get().saturating_add(1u32.into()));

Expand All @@ -131,7 +131,7 @@ benchmarks_instance_pallet! {
let auction_id = NFTAuction::<T, I>::current_auction_id();
let caller = owner.clone();
let expire = T::MinDeadline::get().saturating_mul(2u32.into());
}: _(SystemOrigin::Signed(caller.clone()), class, instance, get_dollars::<T, I>(20), get_dollars::<T, I>(1), expire)
}: _(SystemOrigin::Signed(caller.clone()), class, instance, get_dollars::<T, I>(20), get_dollars::<T, I>(1), expire, None)
verify {
assert_last_event::<T, I>(Event::<T, I>::CreatedEnglishAuction(caller, auction_id).into());
}
Expand All @@ -144,7 +144,7 @@ benchmarks_instance_pallet! {
let (class, instance) = create_nft::<T, I>(&owner);
let auction_id = NFTAuction::<T, I>::current_auction_id();
let expire = T::MinDeadline::get().saturating_mul(2u32.into());
assert_ok!(NFTAuction::<T, I>::create_english(SystemOrigin::Signed(owner.clone()).into(), class, instance, get_dollars::<T, I>(20), get_dollars::<T, I>(1), expire));
assert_ok!(NFTAuction::<T, I>::create_english(SystemOrigin::Signed(owner.clone()).into(), class, instance, get_dollars::<T, I>(20), get_dollars::<T, I>(1), expire, None));

System::<T>::set_block_number(T::MinDeadline::get().saturating_add(1u32.into()));

Expand All @@ -164,7 +164,7 @@ benchmarks_instance_pallet! {
let (class, instance) = create_nft::<T, I>(&owner);
let auction_id = NFTAuction::<T, I>::current_auction_id();
let expire = T::MinDeadline::get().saturating_mul(2u32.into());
assert_ok!(NFTAuction::<T, I>::create_english(SystemOrigin::Signed(owner.clone()).into(), class, instance, get_dollars::<T, I>(20), get_dollars::<T, I>(1), expire));
assert_ok!(NFTAuction::<T, I>::create_english(SystemOrigin::Signed(owner.clone()).into(), class, instance, get_dollars::<T, I>(20), get_dollars::<T, I>(1), expire, None));

System::<T>::set_block_number(T::MinDeadline::get().saturating_add(1u32.into()));

Expand All @@ -188,7 +188,7 @@ benchmarks_instance_pallet! {
let (class, instance) = create_nft::<T, I>(&owner);
let auction_id = NFTAuction::<T, I>::current_auction_id();
let expire = T::MinDeadline::get().saturating_mul(2u32.into());
assert_ok!(NFTAuction::<T, I>::create_english(SystemOrigin::Signed(owner.clone()).into(), class, instance, get_dollars::<T, I>(20), get_dollars::<T, I>(1), expire));
assert_ok!(NFTAuction::<T, I>::create_english(SystemOrigin::Signed(owner.clone()).into(), class, instance, get_dollars::<T, I>(20), get_dollars::<T, I>(1), expire, None));

System::<T>::set_block_number(T::MinDeadline::get().saturating_add(1u32.into()));

Expand Down
108 changes: 102 additions & 6 deletions pallets/nft-auction/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ pub mod mock;
mod tests;
pub mod weights;

pub mod migrations;

use codec::{Decode, Encode, HasCompact};
use frame_support::{
dispatch::DispatchResult,
traits::{Currency, Get, ReservableCurrency},
weights::Weight,
};
use frame_system::pallet_prelude::BlockNumberFor;
use sp_runtime::{
Expand All @@ -22,6 +25,19 @@ use sp_runtime::{
pub use pallet::*;
pub use weights::WeightInfo;

pub(crate) const LOG_TARGET: &'static str = "runtime::nft_auction";

// syntactic sugar for logging.
#[macro_export]
macro_rules! log {
($level:tt, $patter:expr $(, $values:expr)* $(,)?) => {
log::$level!(
target: crate::LOG_TARGET,
concat!("[{:?}] 💸 ", $patter), <frame_system::Pallet<T>>::block_number() $(, $values)*
)
};
}

pub type BalanceOf<T, I = ()> = <<T as pallet_nft::Config<I>>::Currency as Currency<
<T as frame_system::Config>::AccountId,
>>::Balance;
Expand Down Expand Up @@ -71,6 +87,9 @@ pub struct DutchAuction<AccountId, ClassId, InstanceId, Balance, BlockNumber> {
/// When creating auction
#[codec(compact)]
pub created_at: BlockNumber,
/// When opening auction
#[codec(compact)]
pub open_at: BlockNumber,
/// The auction should be forced to be ended if current block number higher than this value.
#[codec(compact)]
pub deadline: BlockNumber,
Expand Down Expand Up @@ -100,6 +119,9 @@ pub struct EnglishAuction<AccountId, ClassId, InstanceId, Balance, BlockNumber>
/// When creating auction
#[codec(compact)]
pub created_at: BlockNumber,
/// When opening auction
#[codec(compact)]
pub open_at: BlockNumber,
/// The auction should be forced to be ended if current block number higher than this value.
#[codec(compact)]
pub deadline: BlockNumber,
Expand All @@ -117,6 +139,21 @@ pub struct AuctionBid<AccountId, Balance, BlockNumber> {
pub bid_at: BlockNumber,
}

// A value placed in storage that represents the current version of the Scheduler storage.
// This value is used by the `on_runtime_upgrade` logic to determine whether we run
// storage migration logic.
#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug)]
pub enum Releases {
V0,
V1,
}

impl Default for Releases {
fn default() -> Self {
Releases::V0
}
}

#[frame_support::pallet]
pub mod pallet {
use super::*;
Expand Down Expand Up @@ -192,6 +229,12 @@ pub mod pallet {
pub type CurrentAuctionId<T: Config<I>, I: 'static = ()> =
StorageValue<_, T::AuctionId, ValueQuery>;

/// Storage version of the pallet.
///
/// New networks start with last version.
#[pallet::storage]
pub type StorageVersion<T: Config<I>, I: 'static = ()> = StorageValue<_, Releases, ValueQuery>;

#[pallet::event]
#[pallet::metadata(
T::AccountId = "AccountId",
Expand Down Expand Up @@ -226,6 +269,7 @@ pub mod pallet {
InvalidDeadline,
InvalidPrice,
InvalidNextAuctionId,
AuctionNotOpen,
AuctionNotFound,
AuctionBidNotFound,
AuctionClosed,
Expand All @@ -239,6 +283,52 @@ pub mod pallet {
CannotRemoveAuction,
}

#[pallet::genesis_config]
pub struct GenesisConfig;

#[cfg(feature = "std")]
impl Default for GenesisConfig {
fn default() -> Self {
Self
}
}

#[pallet::genesis_build]
impl<T: Config<I>, I: 'static> GenesisBuild<T, I> for GenesisConfig {
fn build(&self) {
StorageVersion::<T, I>::put(Releases::V1);
}
}

#[pallet::hooks]
impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
fn on_runtime_upgrade() -> Weight {
if StorageVersion::<T, I>::get() == Releases::V0 {
migrations::v1::migrate::<T, I>()
} else {
T::DbWeight::get().reads(1)
}
}

#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<(), &'static str> {
if StorageVersion::<T, I>::get() == Releases::V0 {
migrations::v1::pre_migrate::<T, I>()
} else {
Ok(())
}
}

#[cfg(feature = "try-runtime")]
fn post_upgrade() -> Result<(), &'static str> {
if StorageVersion::<T, I>::get() == Releases::V1 {
migrations::v1::post_migrate::<T, I>()
} else {
Ok(())
}
}
}

#[pallet::call]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// Create an dutch auction.
Expand All @@ -250,6 +340,7 @@ pub mod pallet {
#[pallet::compact] min_price: BalanceOf<T, I>,
#[pallet::compact] max_price: BalanceOf<T, I>,
#[pallet::compact] deadline: BlockNumberFor<T>,
open_at: Option<BlockNumberFor<T>>,
) -> DispatchResult {
let who = ensure_signed(origin)?;
ensure!(
Expand All @@ -258,13 +349,14 @@ pub mod pallet {
);
ensure!(deadline >= T::MinDeadline::get(), Error::<T, I>::InvalidDeadline);
ensure!(max_price > min_price, Error::<T, I>::InvalidPrice);
let now = frame_system::Pallet::<T>::block_number();
let open_at = open_at.map(|v| v.max(now)).unwrap_or(now);

let deposit = T::AuctionDeposit::get();
T::Currency::reserve(&who, deposit)?;
pallet_nft::Pallet::<T, I>::reserve(&class, &instance, &who)?;

let auction_id = Self::gen_auction_id()?;
let now = frame_system::Pallet::<T>::block_number();

let auction = DutchAuction {
owner: who.clone(),
Expand All @@ -273,6 +365,7 @@ pub mod pallet {
min_price,
max_price,
created_at: now,
open_at,
deadline,
deposit,
};
Expand All @@ -297,10 +390,11 @@ pub mod pallet {
let auction =
DutchAuctions::<T, I>::get(auction_id).ok_or(Error::<T, I>::AuctionNotFound)?;
ensure!(auction.owner != who, Error::<T, I>::SelfBid);
let now = frame_system::Pallet::<T>::block_number();
ensure!(auction.open_at <= now, Error::<T, I>::AuctionNotOpen);
let maybe_bid = DutchAuctionBids::<T, I>::get(auction_id);
match (maybe_bid, price) {
(None, price) => {
let now = frame_system::Pallet::<T>::block_number();
ensure!(auction.deadline >= now, Error::<T, I>::AuctionClosed);
let mut new_price = Self::get_dutch_price(&auction, now);
if let Some(bid_price) = price {
Expand All @@ -321,7 +415,6 @@ pub mod pallet {
}
},
(Some(bid), Some(bid_price)) => {
let now = frame_system::Pallet::<T>::block_number();
ensure!(
bid.bid_at.saturating_add(T::DelayOfAuction::get()) >= now,
Error::<T, I>::AuctionClosed
Expand Down Expand Up @@ -394,20 +487,22 @@ pub mod pallet {
#[pallet::compact] init_price: BalanceOf<T, I>,
#[pallet::compact] min_raise_price: BalanceOf<T, I>,
#[pallet::compact] deadline: BlockNumberFor<T>,
open_at: Option<BlockNumberFor<T>>,
) -> DispatchResult {
let who = ensure_signed(origin)?;
ensure!(
pallet_nft::Pallet::<T, I>::validate(&class, &instance, &who),
Error::<T, I>::InvalidNFT
);
ensure!(deadline >= T::MinDeadline::get(), Error::<T, I>::InvalidDeadline);
let now = frame_system::Pallet::<T>::block_number();
let open_at = open_at.map(|v| v.max(now)).unwrap_or(now);

let deposit = T::AuctionDeposit::get();
T::Currency::reserve(&who, deposit)?;
pallet_nft::Pallet::<T, I>::reserve(&class, &instance, &who)?;

let auction_id = Self::gen_auction_id()?;
let now = frame_system::Pallet::<T>::block_number();

let auction = EnglishAuction {
owner: who.clone(),
Expand All @@ -416,6 +511,7 @@ pub mod pallet {
init_price,
min_raise_price,
created_at: now,
open_at,
deadline,
deposit,
};
Expand All @@ -438,10 +534,11 @@ pub mod pallet {
let auction =
EnglishAuctions::<T, I>::get(auction_id).ok_or(Error::<T, I>::AuctionNotFound)?;
ensure!(auction.owner != who, Error::<T, I>::SelfBid);
let now = frame_system::Pallet::<T>::block_number();
ensure!(auction.open_at <= now, Error::<T, I>::AuctionNotOpen);
let maybe_bid = EnglishAuctionBids::<T, I>::get(auction_id);
match maybe_bid {
None => {
let now = frame_system::Pallet::<T>::block_number();
ensure!(auction.deadline >= now, Error::<T, I>::AuctionClosed);
T::Currency::reserve(&who, price)?;
EnglishAuctionBids::<T, I>::insert(
Expand All @@ -451,7 +548,6 @@ pub mod pallet {
Self::deposit_event(Event::BidEnglishAuction(who, auction_id));
},
Some(bid) => {
let now = frame_system::Pallet::<T>::block_number();
ensure!(
bid.bid_at.saturating_add(T::DelayOfAuction::get()) >= now,
Error::<T, I>::AuctionClosed
Expand Down
Loading