Преглед на файлове

VNFT auction init, add start_video_auction extrinsic

iorveth преди 3 години
родител
ревизия
22701c0ab5

+ 20 - 0
Cargo.lock

@@ -2435,6 +2435,7 @@ dependencies = [
  "pallet-transaction-payment",
  "pallet-transaction-payment-rpc-runtime-api",
  "pallet-utility",
+ "pallet-vnft-auction",
  "pallet-working-group",
  "parity-scale-codec",
  "serde",
@@ -4334,6 +4335,25 @@ dependencies = [
  "sp-std",
 ]
 
+[[package]]
+name = "pallet-vnft-auction"
+version = "3.1.1"
+dependencies = [
+ "frame-support",
+ "frame-system",
+ "pallet-balances",
+ "pallet-common",
+ "pallet-content",
+ "pallet-timestamp",
+ "parity-scale-codec",
+ "serde",
+ "sp-arithmetic",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+]
+
 [[package]]
 name = "pallet-working-group"
 version = "3.1.1"

+ 1 - 0
Cargo.toml

@@ -16,6 +16,7 @@ members = [
 	"runtime-modules/token-minting",
 	"runtime-modules/working-group",
 	"runtime-modules/content",
+	"runtime-modules/vnft-auction",
 	"node",
 	"utils/chain-spec-builder/"
 ]

+ 1 - 0
runtime-modules/common/src/storage.rs

@@ -55,3 +55,4 @@ pub trait StorageSystem<T: crate::StorageOwnership + crate::MembershipTypes> {
         content_ids: &[T::ContentId],
     ) -> DispatchResult;
 }
+

+ 36 - 0
runtime-modules/vnft-auction/Cargo.toml

@@ -0,0 +1,36 @@
+[package]
+name = 'pallet-vnft-auction'
+version = '3.1.1'
+authors = ['Joystream contributors']
+edition = '2018'
+
+[dependencies]
+serde = {version = '1.0.101', features = ['derive'], optional = true}
+sp-std = { package = 'sp-std', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '2cd20966cc09b059817c3ebe12fc130cdd850d62'}
+sp-runtime = { package = 'sp-runtime', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '2cd20966cc09b059817c3ebe12fc130cdd850d62'}
+frame-support = { package = 'frame-support', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '2cd20966cc09b059817c3ebe12fc130cdd850d62'}
+frame-system = { package = 'frame-system', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '2cd20966cc09b059817c3ebe12fc130cdd850d62'}
+sp-arithmetic = { package = 'sp-arithmetic', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '2cd20966cc09b059817c3ebe12fc130cdd850d62'}
+codec = { package = 'parity-scale-codec', version = '1.3.4', default-features = false, features = ['derive'] }
+timestamp = { package = 'pallet-timestamp', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '2cd20966cc09b059817c3ebe12fc130cdd850d62'}
+common = { package = 'pallet-common', default-features = false, path = '../common'}
+content = { package = 'pallet-content', default-features = false, path = '../content'}
+
+[dev-dependencies]
+sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '2cd20966cc09b059817c3ebe12fc130cdd850d62'}
+sp-core = { package = 'sp-core', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '2cd20966cc09b059817c3ebe12fc130cdd850d62'}
+balances = { package = 'pallet-balances', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '2cd20966cc09b059817c3ebe12fc130cdd850d62'}
+
+[features]
+default = ['std']
+std = [
+	'sp-std/std',
+	'sp-runtime/std',
+	'frame-support/std',
+	'frame-system/std',
+	'sp-arithmetic/std',
+	'codec/std',
+	'serde',
+	'common/std',
+	'timestamp/std',
+]

+ 34 - 0
runtime-modules/vnft-auction/src/errors.rs

@@ -0,0 +1,34 @@
+use crate::*;
+use frame_support::decl_error;
+
+decl_error! {
+    /// Content directory errors
+    pub enum Error for Module<T: Trait> {
+        /// Auction does not exist
+        AuctionDoesNotExist,
+        /// Vnft auction for given video_id have been started already
+        AuctionAlreadyStarted,
+        /// VNFT does not exist
+        VNFTDoesNotExist,
+        /// Overflow or underflow error happened
+        OverflowOrUnderflowHappened,
+        /// Actor origin authorization error happened
+        ActorOriginAuthError,
+        /// Actor not authorized to issue nft
+        ActorNotAuthorizedToIssueNft,
+        /// Given origin does not own vnft
+        DoesNotOwnVNFT,
+        /// Royalty Upper Bound Exceeded
+        RoyaltyUpperBoundExceeded,
+        /// Royalty Lower Bound Exceeded
+        RoyaltyLowerBoundExceeded,
+        /// Round time upper bound exceeded
+        RoundTimeUpperBoundExceeded,
+        /// Round time lower bound exceeded
+        RoundTimeLowerBoundExceeded,
+        /// Starting price upper bound exceeded
+        StartingPriceUpperBoundExceeded,
+        /// Starting price lower bound exceeded
+        StartingPriceLowerBoundExceeded,
+    }
+}

+ 198 - 0
runtime-modules/vnft-auction/src/functions.rs

@@ -0,0 +1,198 @@
+use crate::*;
+
+impl<T: Trait> Module<T> {
+    /// Authorize auctioneer
+    pub(crate) fn authorize_auctioneer(
+        origin: T::Origin,
+        actor: &ContentActor<CuratorGroupId<T>, CuratorId<T>, MemberId<T>>,
+        auction_params: &AuctionParams<
+            T::VNFTId,
+            VideoId<T>,
+            <T as timestamp::Trait>::Moment,
+            BalanceOf<T>,
+        >,
+    ) -> Result<T::AccountId, DispatchError> {
+        if let AuctionMode::WithoutIsuance(vnft_id) = auction_params.auction_mode {
+            let vnft = Self::ensure_vnft_exists(vnft_id)?;
+
+            // Only members are supposed to start auctions for already existing nfts
+            if let ContentActor::Member(member_id) = actor {
+                let account_id = T::MemberOriginValidator::ensure_actor_origin(origin, *member_id)
+                    .map_err::<Error<T>, _>(|_| Error::<T>::ActorOriginAuthError.into())?;
+
+                Self::ensure_vnft_ownership(&account_id, &vnft)?;
+
+                Ok(account_id)
+            } else {
+                Err(Error::<T>::AuctionDoesNotExist.into())
+            }
+        } else {
+            // TODO: Move to common pallet
+            content::ensure_actor_authorized_to_create_channel::<T>(origin.clone(), &actor)
+                .map_err::<Error<T>, _>(|_| Error::<T>::ActorNotAuthorizedToIssueNft.into());
+            let account_id = ensure_signed(origin)?;
+            Ok(account_id)
+        }
+    }
+
+    pub(crate) fn validate_auction_params(
+        auction_params: &AuctionParams<
+            T::VNFTId,
+            VideoId<T>,
+            <T as timestamp::Trait>::Moment,
+            BalanceOf<T>,
+        >,
+    ) -> DispatchResult {
+        if let AuctionMode::WithIssuance(Some(royalty), _) = auction_params.auction_mode {
+            Self::ensure_royalty_bounds_satisfied(royalty)?;
+        }
+
+        Self::ensure_round_time_bounds_satisfied(auction_params.round_time)?;
+        Self::ensure_starting_price_bounds_satisfied(auction_params.starting_price)?;
+
+        Ok(())
+    }
+
+    /// Ensure given account id is vnft owner
+    pub(crate) fn ensure_vnft_ownership(
+        account_id: &T::AccountId,
+        vnft: &VNFT<T::AccountId>,
+    ) -> DispatchResult {
+        ensure!(*account_id == vnft.owner, Error::<T>::DoesNotOwnVNFT);
+        Ok(())
+    }
+
+    /// Ensure royalty bounds satisfied
+    pub(crate) fn ensure_royalty_bounds_satisfied(royalty: Perbill) -> DispatchResult {
+        ensure!(
+            royalty <= Self::max_creator_royalty(),
+            Error::<T>::RoyaltyUpperBoundExceeded
+        );
+        ensure!(
+            royalty >= Self::min_creator_royalty(),
+            Error::<T>::RoyaltyLowerBoundExceeded
+        );
+        Ok(())
+    }
+
+    /// Ensure royalty bounds satisfied
+    pub(crate) fn ensure_round_time_bounds_satisfied(round_time: T::Moment) -> DispatchResult {
+        ensure!(
+            round_time <= Self::max_round_time(),
+            Error::<T>::RoundTimeUpperBoundExceeded
+        );
+        ensure!(
+            round_time >= Self::min_round_time(),
+            Error::<T>::RoundTimeLowerBoundExceeded
+        );
+        Ok(())
+    }
+
+    /// Ensure royalty bounds satisfied
+    pub(crate) fn ensure_starting_price_bounds_satisfied(
+        starting_price: BalanceOf<T>,
+    ) -> DispatchResult {
+        ensure!(
+            starting_price >= Self::max_starting_price(),
+            Error::<T>::StartingPriceUpperBoundExceeded
+        );
+        ensure!(
+            starting_price <= Self::min_starting_price(),
+            Error::<T>::StartingPriceLowerBoundExceeded
+        );
+        Ok(())
+    }
+
+    /// Check whether auction for given video id exists
+    pub(crate) fn is_auction_exist(video_id: T::VideoId) -> bool {
+        <AuctionByVideoId<T>>::contains_key(video_id)
+    }
+
+    /// Check whether vnft for given vnft id exists
+    pub(crate) fn is_vnft_exist(vnft_id: T::VNFTId) -> bool {
+        <VNFTById<T>>::contains_key(vnft_id)
+    }
+
+    /// Ensure auction for given video id exists
+    pub(crate) fn ensure_auction_exists(video_id: T::VideoId) -> Result<Auction<T>, Error<T>> {
+        ensure!(
+            Self::is_auction_exist(video_id),
+            Error::<T>::AuctionDoesNotExist
+        );
+        Ok(Self::auction_by_video_id(video_id))
+    }
+
+    /// Ensure given vnft exists
+    pub(crate) fn ensure_vnft_exists(vnft_id: T::VNFTId) -> Result<VNFT<T::AccountId>, Error<T>> {
+        ensure!(Self::is_vnft_exist(vnft_id), Error::<T>::VNFTDoesNotExist);
+        Ok(Self::vnft_by_vnft_id(vnft_id))
+    }
+
+    /// Try complete auction when round time expired
+    pub fn try_complete_auction(auction: Auction<T>, video_id: T::VideoId) -> bool {
+        let now = timestamp::Module::<T>::now();
+        if (now - auction.last_bid_time) >= auction.round_time {
+            Self::complete_auction(auction, video_id);
+            true
+        } else {
+            false
+        }
+    }
+
+    /// Complete auction
+    fn complete_auction(auction: Auction<T>, video_id: T::VideoId) {
+        <AuctionByVideoId<T>>::remove(video_id);
+        match auction.auction_mode.clone() {
+            AuctionMode::WithIssuance(royalty, _) => {
+                <Module<T>>::issue_vnft(auction, video_id, royalty)
+            }
+            AuctionMode::WithoutIsuance(vnft_id) => {
+                <Module<T>>::complete_vnft_auction_transfer(auction, vnft_id)
+            }
+        }
+    }
+
+    /// Issue vnft and update mapping relations
+    pub(crate) fn issue_vnft(auction: Auction<T>, video_id: T::VideoId, royalty: Option<Royalty>) {
+        let vnft_id = Self::next_video_nft_id();
+
+        <VNFTIdByVideo<T>>::insert(video_id, vnft_id);
+
+        let creator_royalty = if let Some(royalty) = royalty {
+            Some((auction.auctioneer_account_id.clone(), royalty))
+        } else {
+            None
+        };
+
+        <VNFTById<T>>::insert(
+            vnft_id,
+            VNFT::new(auction.current_bidder.clone(), creator_royalty),
+        );
+
+        NextVNFTId::<T>::put(vnft_id + T::VNFTId::one());
+
+        Self::deposit_event(RawEvent::NftIssued(video_id, vnft_id, auction));
+    }
+
+    /// Complete vnft transfer
+    pub(crate) fn complete_vnft_auction_transfer(auction: Auction<T>, vnft_id: T::VNFTId) {
+        let vnft = Self::vnft_by_vnft_id(vnft_id);
+        let current_bid = auction.current_bid;
+
+        if let Some((creator_account_id, creator_royalty)) = vnft.creator_royalty {
+            let royalty = creator_royalty * current_bid;
+
+            T::NftCurrencyProvider::slash_reserved(&auction.current_bidder, current_bid);
+
+            T::NftCurrencyProvider::deposit_creating(
+                &auction.auctioneer_account_id,
+                current_bid - royalty,
+            );
+            T::NftCurrencyProvider::deposit_creating(&creator_account_id, royalty);
+        } else {
+            T::NftCurrencyProvider::deposit_creating(&auction.auctioneer_account_id, current_bid);
+        }
+
+        <VNFTById<T>>::mutate(vnft_id, |vnft| vnft.owner = auction.current_bidder);
+    }
+}

+ 175 - 0
runtime-modules/vnft-auction/src/lib.rs

@@ -0,0 +1,175 @@
+// Ensure we're `no_std` when compiling for Wasm.
+#![cfg_attr(not(feature = "std"), no_std)]
+#![recursion_limit = "256"]
+
+mod errors;
+mod functions;
+mod types;
+pub use common::{origin::ActorOriginValidator, MembershipTypes};
+pub use content::{ContentActor, ContentActorAuthenticator};
+use errors::*;
+use frame_support::{
+    decl_event, decl_module, decl_storage,
+    dispatch::{DispatchError, DispatchResult},
+    ensure,
+    traits::Get,
+    Parameter,
+};
+pub use frame_system::ensure_signed;
+pub use functions::*;
+#[cfg(feature = "std")]
+pub use serde::{Deserialize, Serialize};
+use sp_arithmetic::traits::{BaseArithmetic, One, Zero};
+use sp_runtime::traits::{MaybeSerializeDeserialize, Member};
+use types::*;
+
+use codec::Codec;
+pub use codec::{Decode, Encode};
+use frame_support::traits::{Currency, ReservableCurrency};
+pub use sp_runtime::Perbill;
+
+pub trait NumericIdentifier:
+    Parameter
+    + Member
+    + BaseArithmetic
+    + Codec
+    + Default
+    + Copy
+    + Clone
+    + MaybeSerializeDeserialize
+    + Eq
+    + PartialEq
+    + Ord
+    + Zero
+{
+}
+
+impl NumericIdentifier for u64 {}
+
+type VideoId<T> = <T as content::Trait>::VideoId;
+
+pub trait Trait:
+    timestamp::Trait + content::Trait + content::ContentActorAuthenticator + MembershipTypes
+{
+    /// The overarching event type.
+    type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
+
+    // NFT provider pallet
+    type VNFTId: NumericIdentifier;
+
+    // Payment provider
+    type NftCurrencyProvider: Currency<Self::AccountId> + ReservableCurrency<Self::AccountId>;
+
+    // Member origin validator
+    type MemberOriginValidator: ActorOriginValidator<Self::Origin, MemberId<Self>, Self::AccountId>;
+}
+
+decl_storage! {
+    trait Store for Module<T: Trait> as Content {
+
+        /// Map, representing  VideoId -> VNFTId relation
+        pub VNFTIdByVideo get(fn vnft_id_by_video_id): map hasher(blake2_128_concat) VideoId<T> => T::VNFTId;
+
+        /// Map, representing  VNFTId -> VNFT relation
+        pub VNFTById get(fn vnft_by_vnft_id): map hasher(blake2_128_concat) T::VNFTId => VNFT<T::AccountId>;
+
+        /// Map, representing VideoId -> Auction relation
+        pub AuctionByVideoId get (fn auction_by_video_id): map hasher(blake2_128_concat) VideoId<T> => Auction<T>;
+
+        /// Next vNFT id
+        pub NextVNFTId get(fn next_video_nft_id) config(): T::VNFTId;
+
+        /// Min auction round time
+        pub MinRoundTime get(fn min_round_time) config(): T::Moment;
+
+        /// Max auction round time
+        pub MaxRoundTime get(fn max_round_time) config(): T::Moment;
+
+        /// Min auction staring price
+        pub MinStartingPrice get(fn min_starting_price) config(): BalanceOf<T>;
+
+        /// Max auction staring price
+        pub MaxStartingPrice get(fn max_starting_price) config(): BalanceOf<T>;
+
+        /// Min creator royalty
+        pub MinCreatorRoyalty get(fn min_creator_royalty) config(): Perbill;
+
+        /// Max creator royalty
+        pub MaxCreatorRoyalty get(fn max_creator_royalty) config(): Perbill;
+    }
+}
+
+decl_module! {
+    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
+        /// Predefined errors
+        type Error = Error<T>;
+
+        /// Initializing events
+        fn deposit_event() = default;
+
+        /// Start video auction
+        #[weight = 10_000_000] // TODO: adjust weight
+        pub fn start_video_auction(
+            origin,
+            auctioneer: ContentActor<CuratorGroupId<T>, CuratorId<T>, MemberId<T>>,
+            auction_params: AuctionParams<T::VNFTId, VideoId<T>, <T as timestamp::Trait>::Moment, BalanceOf<T>>,
+        ) {
+
+            let auctioneer_account_id = Self::authorize_auctioneer(origin, &auctioneer, &auction_params)?;
+
+            // Validate round_time & starting_price
+            Self::validate_auction_params(&auction_params)?;
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            let video_id = auction_params.video_id;
+
+            // Try complete auction
+            if Self::is_auction_exist(video_id) {
+
+                let auction = Self::auction_by_video_id(video_id);
+
+                // Try finalize already completed auction (issues new nft if required)
+                ensure!(Self::try_complete_auction(auction, video_id), Error::<T>::AuctionAlreadyStarted);
+                return Ok(())
+            }
+
+            let auction = AuctionRecord::new::<T::VideoId>(auctioneer, auctioneer_account_id, auction_params.clone());
+
+            <AuctionByVideoId<T>>::insert(video_id, auction);
+
+            // Trigger event
+            Self::deposit_event(RawEvent::AuctionStarted(auctioneer, auction_params));
+        }
+    }
+}
+
+decl_event!(
+    pub enum Event<T>
+    where
+        VideoId = <T as content::Trait>::VideoId,
+        VNFTId = <T as Trait>::VNFTId,
+        ContentActor = ContentActor<CuratorGroupId<T>, CuratorId<T>, MemberId<T>>,
+        AuctionParams = AuctionParams<
+            <T as Trait>::VNFTId,
+            VideoId<T>,
+            <T as timestamp::Trait>::Moment,
+            BalanceOf<T>,
+        >,
+        Auction = AuctionRecord<
+            <T as frame_system::Trait>::AccountId,
+            <T as Trait>::VNFTId,
+            <T as timestamp::Trait>::Moment,
+            CuratorGroupId<T>,
+            CuratorId<T>,
+            MemberId<T>,
+            BalanceOf<T>,
+        >,
+    {
+        // Curators
+        AuctionStarted(ContentActor, AuctionParams),
+        NftIssued(VideoId, VNFTId, Auction),
+    }
+);

+ 127 - 0
runtime-modules/vnft-auction/src/types.rs

@@ -0,0 +1,127 @@
+use super::*;
+
+// Metadata for vNFT issuance
+type Metadata = Vec<u8>;
+
+pub type BalanceOf<T> =
+    <<T as Trait>::NftCurrencyProvider as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
+
+pub type CuratorGroupId<T> = <T as ContentActorAuthenticator>::CuratorGroupId;
+pub type CuratorId<T> = <T as ContentActorAuthenticator>::CuratorId;
+pub type MemberId<T> = <T as MembershipTypes>::MemberId;
+
+// Owner royalty
+pub type Royalty = Perbill;
+
+// Either new auction, which requires vNFT issance or auction for already existing nft.
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
+pub enum AuctionMode<VNFTId: Default> {
+    // Auction, where nft issued at the end
+    WithIssuance(Option<Royalty>, Metadata),
+    // Auction for already existing nft
+    WithoutIsuance(VNFTId),
+}
+
+impl<VNFTId: Default> Default for AuctionMode<VNFTId> {
+    fn default() -> Self {
+        Self::WithoutIsuance(VNFTId::default())
+    }
+}
+
+/// Information on the auction being created.
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
+pub struct AuctionRecord<
+    AccountId,
+    VNFTId: Default,
+    Moment: BaseArithmetic + Copy,
+    CuratorGroupId: Default + Copy,
+    CuratorId: Default + Copy,
+    MemberId: Default + Copy,
+    Balance,
+> {
+    pub auctioneer: ContentActor<CuratorGroupId, CuratorId, MemberId>,
+    pub auctioneer_account_id: AccountId,
+    pub auction_mode: AuctionMode<VNFTId>,
+    pub starting_price: Balance,
+    pub buy_now_price: Option<Balance>,
+    pub round_time: Moment,
+    pub last_bid_time: Moment,
+    pub current_bid: Balance,
+    pub current_bidder: AccountId,
+}
+
+impl<
+        AccountId: Default,
+        VNFTId: Default,
+        Moment: BaseArithmetic + Copy + Default,
+        CuratorGroupId: Default + Copy,
+        CuratorId: Default + Copy,
+        MemberId: Default + Copy,
+        Balance: Default,
+    > AuctionRecord<AccountId, VNFTId, Moment, CuratorGroupId, CuratorId, MemberId, Balance>
+{
+    pub fn new<VideoId>(
+        auctioneer: ContentActor<CuratorGroupId, CuratorId, MemberId>,
+        auctioneer_account_id: AccountId,
+        auction_params: AuctionParams<VNFTId, VideoId, Moment, Balance>,
+    ) -> Self {
+        let AuctionParams {
+            auction_mode,
+            round_time,
+            starting_price,
+            buy_now_price,
+            ..
+        } = auction_params;
+        Self {
+            auctioneer,
+            auctioneer_account_id,
+            auction_mode,
+            starting_price,
+            buy_now_price,
+            round_time,
+            last_bid_time: Moment::default(),
+            current_bid: Balance::default(),
+            current_bidder: AccountId::default(),
+        }
+    }
+}
+
+/// Auction alias type for simplification.
+pub type Auction<T> = AuctionRecord<
+    <T as frame_system::Trait>::AccountId,
+    <T as Trait>::VNFTId,
+    <T as timestamp::Trait>::Moment,
+    CuratorGroupId<T>,
+    CuratorId<T>,
+    MemberId<T>,
+    BalanceOf<T>,
+>;
+
+/// Parameters, needed for auction start
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
+pub struct AuctionParams<VNFTId: Default, VideoId, Moment, Balance> {
+    pub auction_mode: AuctionMode<VNFTId>,
+    pub video_id: VideoId,
+    pub round_time: Moment,
+    pub starting_price: Balance,
+    pub buy_now_price: Option<Balance>,
+}
+
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
+pub struct VNFT<AccountId: Default> {
+    pub owner: AccountId,
+    pub creator_royalty: Option<(AccountId, Royalty)>,
+}
+
+impl<AccountId: Default> VNFT<AccountId> {
+    pub fn new(owner: AccountId, creator_royalty: Option<(AccountId, Royalty)>) -> Self {
+        Self {
+            owner,
+            creator_royalty,
+        }
+    }
+}

+ 2 - 0
runtime/Cargo.toml

@@ -75,6 +75,7 @@ proposals-engine = { package = 'pallet-proposals-engine', default-features = fal
 proposals-discussion = { package = 'pallet-proposals-discussion', default-features = false, path = '../runtime-modules/proposals/discussion'}
 proposals-codex = { package = 'pallet-proposals-codex', default-features = false, path = '../runtime-modules/proposals/codex'}
 content = { package = 'pallet-content', default-features = false, path = '../runtime-modules/content' }
+vnft-auction = { package = 'pallet-vnft-auction', default-features = false, path = '../runtime-modules/vnft-auction' }
 
 [dev-dependencies]
 sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '2cd20966cc09b059817c3ebe12fc130cdd850d62'}
@@ -148,6 +149,7 @@ std = [
     'proposals-discussion/std',
     'proposals-codex/std',
     'content/std',
+    'vnft-auction/std',
 ]
 runtime-benchmarks = [
     "frame-system/runtime-benchmarks",