Set Price Extrinsic
Now that we have our Pallet set up to handle balances, let's actually use them.
In this step, we will create an extrinsic which allows the owner of a kitty to set a price for the kitty.
Set Price
The set price extrinsic is pretty straight forward.
As we noted in the last step, we allow the price
field of a kitty to be Option<BalanceOf<T>>
, where None
denotes a kitty which is not for sale, and Some(price)
denotes a kitty which is for sale at some price
.
With this in mind, our set_price
extrinsic will also accept an Option<BalanceOf<T>>
so that a user can put the kitty on or off the market.
Your Turn
In this step, you will scaffold the extrinsic and internal functions for set_price
.
- Create a new event
PriceSet
with the fields noted in the template. - Create a new extrinsic
set_price
with the params noted in the template. - Create a new internal function
do_set_price
which simply deposits thePriceSet
event.
We will actually populate the logic for do_set_price
in the next step.
#![allow(unused)] fn main() { use super::*; use frame::prelude::*; use frame::primitives::BlakeTwo256; use frame::traits::Hash; // Learn about internal functions. impl<T: Config> Pallet<T> { // Generates and returns DNA pub fn gen_dna() -> [u8; 32] { // Create randomness payload. Multiple kitties can be generated in the same block, // retaining uniqueness. let unique_payload = ( frame_system::Pallet::<T>::parent_hash(), frame_system::Pallet::<T>::block_number(), frame_system::Pallet::<T>::extrinsic_index(), CountForKitties::<T>::get(), ); BlakeTwo256::hash_of(&unique_payload).into() } pub fn mint(owner: T::AccountId, dna: [u8; 32]) -> DispatchResult { let kitty = Kitty { dna, owner: owner.clone(), price: None }; // Check if the kitty does not already exist in our storage map ensure!(!Kitties::<T>::contains_key(dna), Error::<T>::DuplicateKitty); let current_count: u32 = CountForKitties::<T>::get(); let new_count = current_count.checked_add(1).ok_or(Error::<T>::TooManyKitties)?; KittiesOwned::<T>::try_append(&owner, dna).map_err(|_| Error::<T>::TooManyOwned)?; Kitties::<T>::insert(dna, kitty); CountForKitties::<T>::set(new_count); Self::deposit_event(Event::<T>::Created { owner }); Ok(()) } pub fn do_transfer(from: T::AccountId, to: T::AccountId, kitty_id: [u8; 32]) -> DispatchResult { ensure!(from != to, Error::<T>::TransferToSelf); let mut kitty = Kitties::<T>::get(kitty_id).ok_or(Error::<T>::NoKitty)?; ensure!(kitty.owner == from, Error::<T>::NotOwner); kitty.owner = to.clone(); kitty.price = None; let mut to_owned = KittiesOwned::<T>::get(&to); to_owned.try_push(kitty_id).map_err(|_| Error::<T>::TooManyOwned)?; let mut from_owned = KittiesOwned::<T>::get(&from); if let Some(ind) = from_owned.iter().position(|&id| id == kitty_id) { from_owned.swap_remove(ind); } else { return Err(Error::<T>::NoKitty.into()) } Kitties::<T>::insert(kitty_id, kitty); KittiesOwned::<T>::insert(&to, to_owned); KittiesOwned::<T>::insert(&from, from_owned); Self::deposit_event(Event::<T>::Transferred { from, to, kitty_id }); Ok(()) } /* 🚧 TODO 🚧: Make an internal function called `do_set_price`: - Inputs to the function are: - `caller` which is `T::AccountId`. - `kitty_id` which is `[u8; 32]`. - `new_price` which is `Option<BalanceOf<T>`. - Returns a `DispatchResult`. - The internal logic, for now, should be: - `Self::deposit_event` for `Event::<T>::PriceSet` with the appropriate params. - Return `Ok(())`. */ } }
#![allow(unused)] #![cfg_attr(not(feature = "std"), no_std)] fn main() { mod impls; mod tests; use frame::prelude::*; use frame::traits::fungible::Inspect; use frame::traits::fungible::Mutate; pub use pallet::*; #[frame::pallet(dev_mode)] pub mod pallet { use super::*; #[pallet::pallet] pub struct Pallet<T>(core::marker::PhantomData<T>); #[pallet::config] pub trait Config: frame_system::Config { type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; /// The Fungible handler for the kitties pallet. type NativeBalance: Inspect<Self::AccountId> + Mutate<Self::AccountId>; } // Allows easy access our Pallet's `Balance` type. Comes from `Fungible` interface. pub type BalanceOf<T> = <<T as Config>::NativeBalance as Inspect<<T as frame_system::Config>::AccountId>>::Balance; #[derive(Encode, Decode, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(T))] pub struct Kitty<T: Config> { // Using 32 bytes to represent a kitty DNA pub dna: [u8; 32], pub owner: T::AccountId, pub price: Option<BalanceOf<T>>, } #[pallet::storage] pub(super) type CountForKitties<T: Config> = StorageValue<Value = u32, QueryKind = ValueQuery>; #[pallet::storage] pub(super) type Kitties<T: Config> = StorageMap<Key = [u8; 32], Value = Kitty<T>>; /// Track the kitties owned by each account. #[pallet::storage] pub(super) type KittiesOwned<T: Config> = StorageMap< Key = T::AccountId, Value = BoundedVec<[u8; 32], ConstU32<100>>, QueryKind = ValueQuery, >; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event<T: Config> { Created { owner: T::AccountId }, Transferred { from: T::AccountId, to: T::AccountId, kitty_id: [u8; 32] }, /* 🚧 TODO 🚧: Create a new `Event` called `PriceSet` with fields: - `owner` which is `T::AccountId`. - `kitty_id` which is `[u8; 32]`. - `new_price` which is `Option<BalanceOf<T>>`. */ } #[pallet::error] pub enum Error<T> { TooManyKitties, DuplicateKitty, TooManyOwned, TransferToSelf, NoKitty, NotOwner, } #[pallet::call] impl<T: Config> Pallet<T> { pub fn create_kitty(origin: OriginFor<T>) -> DispatchResult { let who = ensure_signed(origin)?; let dna = Self::gen_dna(); Self::mint(who, dna)?; Ok(()) } pub fn transfer( origin: OriginFor<T>, to: T::AccountId, kitty_id: [u8; 32], ) -> DispatchResult { let who = ensure_signed(origin)?; Self::do_transfer(who, to, kitty_id)?; Ok(()) } /* 🚧 TODO 🚧: Make an callable function called `set_price`: - Inputs to the function are: - `origin` which is `OriginFor<T>`. - `kitty_id` which is `[u8; 32]`. - `new_price` which is `Option<BalanceOf<T>`. - Returns a `DispatchResult` - The internal logic, for now, should be: - Extract the caller `who` with `ensure_signed`. - Call `Self::do_set_price` with the appropriate parameters, propagating the result. - Return `Ok(())`. */ } } }
#![allow(unused)] fn main() { use super::*; use frame::prelude::*; use frame::primitives::BlakeTwo256; use frame::traits::Hash; // Learn about internal functions. impl<T: Config> Pallet<T> { // Generates and returns DNA pub fn gen_dna() -> [u8; 32] { // Create randomness payload. Multiple kitties can be generated in the same block, // retaining uniqueness. let unique_payload = ( frame_system::Pallet::<T>::parent_hash(), frame_system::Pallet::<T>::block_number(), frame_system::Pallet::<T>::extrinsic_index(), CountForKitties::<T>::get(), ); BlakeTwo256::hash_of(&unique_payload).into() } pub fn mint(owner: T::AccountId, dna: [u8; 32]) -> DispatchResult { let kitty = Kitty { dna, owner: owner.clone(), price: None }; // Check if the kitty does not already exist in our storage map ensure!(!Kitties::<T>::contains_key(dna), Error::<T>::DuplicateKitty); let current_count: u32 = CountForKitties::<T>::get(); let new_count = current_count.checked_add(1).ok_or(Error::<T>::TooManyKitties)?; KittiesOwned::<T>::try_append(&owner, dna).map_err(|_| Error::<T>::TooManyOwned)?; Kitties::<T>::insert(dna, kitty); CountForKitties::<T>::set(new_count); Self::deposit_event(Event::<T>::Created { owner }); Ok(()) } pub fn do_transfer(from: T::AccountId, to: T::AccountId, kitty_id: [u8; 32]) -> DispatchResult { ensure!(from != to, Error::<T>::TransferToSelf); let mut kitty = Kitties::<T>::get(kitty_id).ok_or(Error::<T>::NoKitty)?; ensure!(kitty.owner == from, Error::<T>::NotOwner); kitty.owner = to.clone(); kitty.price = None; let mut to_owned = KittiesOwned::<T>::get(&to); to_owned.try_push(kitty_id).map_err(|_| Error::<T>::TooManyOwned)?; let mut from_owned = KittiesOwned::<T>::get(&from); if let Some(ind) = from_owned.iter().position(|&id| id == kitty_id) { from_owned.swap_remove(ind); } else { return Err(Error::<T>::NoKitty.into()) } Kitties::<T>::insert(kitty_id, kitty); KittiesOwned::<T>::insert(&to, to_owned); KittiesOwned::<T>::insert(&from, from_owned); Self::deposit_event(Event::<T>::Transferred { from, to, kitty_id }); Ok(()) } pub fn do_set_price( caller: T::AccountId, kitty_id: [u8; 32], new_price: Option<BalanceOf<T>>, ) -> DispatchResult { Self::deposit_event(Event::<T>::PriceSet { owner: caller, kitty_id, new_price }); Ok(()) } } }
#![allow(unused)] #![cfg_attr(not(feature = "std"), no_std)] fn main() { mod impls; mod tests; use frame::prelude::*; use frame::traits::fungible::Inspect; use frame::traits::fungible::Mutate; pub use pallet::*; #[frame::pallet(dev_mode)] pub mod pallet { use super::*; #[pallet::pallet] pub struct Pallet<T>(core::marker::PhantomData<T>); #[pallet::config] pub trait Config: frame_system::Config { type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; /// The Fungible handler for the kitties pallet. type NativeBalance: Inspect<Self::AccountId> + Mutate<Self::AccountId>; } // Allows easy access our Pallet's `Balance` type. Comes from `Fungible` interface. pub type BalanceOf<T> = <<T as Config>::NativeBalance as Inspect<<T as frame_system::Config>::AccountId>>::Balance; #[derive(Encode, Decode, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(T))] pub struct Kitty<T: Config> { // Using 32 bytes to represent a kitty DNA pub dna: [u8; 32], pub owner: T::AccountId, pub price: Option<BalanceOf<T>>, } #[pallet::storage] pub(super) type CountForKitties<T: Config> = StorageValue<Value = u32, QueryKind = ValueQuery>; #[pallet::storage] pub(super) type Kitties<T: Config> = StorageMap<Key = [u8; 32], Value = Kitty<T>>; /// Track the kitties owned by each account. #[pallet::storage] pub(super) type KittiesOwned<T: Config> = StorageMap< Key = T::AccountId, Value = BoundedVec<[u8; 32], ConstU32<100>>, QueryKind = ValueQuery, >; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event<T: Config> { Created { owner: T::AccountId }, Transferred { from: T::AccountId, to: T::AccountId, kitty_id: [u8; 32] }, PriceSet { owner: T::AccountId, kitty_id: [u8; 32], new_price: Option<BalanceOf<T>> }, } #[pallet::error] pub enum Error<T> { TooManyKitties, DuplicateKitty, TooManyOwned, TransferToSelf, NoKitty, NotOwner, } #[pallet::call] impl<T: Config> Pallet<T> { pub fn create_kitty(origin: OriginFor<T>) -> DispatchResult { let who = ensure_signed(origin)?; let dna = Self::gen_dna(); Self::mint(who, dna)?; Ok(()) } pub fn transfer( origin: OriginFor<T>, to: T::AccountId, kitty_id: [u8; 32], ) -> DispatchResult { let who = ensure_signed(origin)?; Self::do_transfer(who, to, kitty_id)?; Ok(()) } pub fn set_price( origin: OriginFor<T>, kitty_id: [u8; 32], new_price: Option<BalanceOf<T>>, ) -> DispatchResult { let who = ensure_signed(origin)?; Self::do_set_price(who, kitty_id, new_price)?; Ok(()) } } } }
#![allow(unused)] fn main() { // Tests for the Kitties Pallet. // // Normally this file would be split into two parts: `mock.rs` and `tests.rs`. // The `mock.rs` file would contain all the setup code for our `TestRuntime`. // Then `tests.rs` would only have the tests for our pallet. // However, to minimize the project, these have been combined into this single file. // // Learn more about creating tests for Pallets: // https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/guides/your_first_pallet/index.html // This flag tells rust to only run this file when running `cargo test`. #![cfg(test)] use crate as pallet_kitties; use crate::*; use frame::deps::sp_io; use frame::runtime::prelude::*; use frame::testing_prelude::*; use frame::traits::fungible::*; type Balance = u64; type Block = frame_system::mocking::MockBlock<TestRuntime>; // In our "test runtime", we represent a user `AccountId` with a `u64`. // This is just a simplification so that we don't need to generate a bunch of proper cryptographic // public keys when writing tests. It is just easier to say "user 1 transfers to user 2". // We create the constants `ALICE` and `BOB` to make it clear when we are representing users below. const ALICE: u64 = 1; const BOB: u64 = 2; const DEFAULT_KITTY: Kitty<TestRuntime> = Kitty { dna: [0u8; 32], owner: 0, price: None }; // Our blockchain tests only need 3 Pallets: // 1. System: Which is included with every FRAME runtime. // 2. PalletBalances: Which is manages your blockchain's native currency. (i.e. DOT on Polkadot) // 3. PalletKitties: The pallet you are building in this tutorial! construct_runtime! { pub struct TestRuntime { System: frame_system, PalletBalances: pallet_balances, PalletKitties: pallet_kitties, } } // Normally `System` would have many more configurations, but you can see that we use some macro // magic to automatically configure most of the pallet for a "default test configuration". #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for TestRuntime { type Block = Block; type AccountData = pallet_balances::AccountData<Balance>; } // Normally `pallet_balances` would have many more configurations, but you can see that we use some // macro magic to automatically configure most of the pallet for a "default test configuration". #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for TestRuntime { type AccountStore = System; type Balance = Balance; } // This is the configuration of our Pallet! If you make changes to the pallet's `trait Config`, you // will also need to update this configuration to represent that. impl pallet_kitties::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type NativeBalance = PalletBalances; } // We need to run most of our tests using this function: `new_test_ext().execute_with(|| { ... });` // It simulates the blockchain database backend for our tests. // If you forget to include this and try to access your Pallet storage, you will get an error like: // "`get_version_1` called outside of an Externalities-provided environment." pub fn new_test_ext() -> sp_io::TestExternalities { frame_system::GenesisConfig::<TestRuntime>::default() .build_storage() .unwrap() .into() } #[test] fn starting_template_is_sane() { new_test_ext().execute_with(|| { let event = Event::<TestRuntime>::Created { owner: ALICE }; let _runtime_event: RuntimeEvent = event.into(); let _call = Call::<TestRuntime>::create_kitty {}; let result = PalletKitties::create_kitty(RuntimeOrigin::signed(BOB)); assert_ok!(result); }); } #[test] fn system_and_balances_work() { // This test will just sanity check that we can access `System` and `PalletBalances`. new_test_ext().execute_with(|| { // We often need to set `System` to block 1 so that we can see events. System::set_block_number(1); // We often need to add some balance to a user to test features which needs tokens. assert_ok!(PalletBalances::mint_into(&ALICE, 100)); assert_ok!(PalletBalances::mint_into(&BOB, 100)); }); } #[test] fn create_kitty_checks_signed() { new_test_ext().execute_with(|| { // The `create_kitty` extrinsic should work when being called by a user. assert_ok!(PalletKitties::create_kitty(RuntimeOrigin::signed(ALICE))); // The `create_kitty` extrinsic should fail when being called by an unsigned message. assert_noop!(PalletKitties::create_kitty(RuntimeOrigin::none()), DispatchError::BadOrigin); }) } #[test] fn create_kitty_emits_event() { new_test_ext().execute_with(|| { // We need to set block number to 1 to view events. System::set_block_number(1); // Execute our call, and ensure it is successful. assert_ok!(PalletKitties::create_kitty(RuntimeOrigin::signed(ALICE))); // Assert the last event by our blockchain is the `Created` event with the correct owner. System::assert_last_event(Event::<TestRuntime>::Created { owner: 1 }.into()); }) } #[test] fn count_for_kitties_created_correctly() { new_test_ext().execute_with(|| { // Querying storage before anything is set will return `0`. assert_eq!(CountForKitties::<TestRuntime>::get(), 0); // You can `set` the value using an `u32`. CountForKitties::<TestRuntime>::set(1337u32); // You can `put` the value directly with a `u32`. CountForKitties::<TestRuntime>::put(1337u32); }) } #[test] fn mint_increments_count_for_kitty() { new_test_ext().execute_with(|| { // Querying storage before anything is set will return `0`. assert_eq!(CountForKitties::<TestRuntime>::get(), 0); // Call `create_kitty` which will call `mint`. assert_ok!(PalletKitties::create_kitty(RuntimeOrigin::signed(ALICE))); // Now the storage should be `1` assert_eq!(CountForKitties::<TestRuntime>::get(), 1); }) } #[test] fn mint_errors_when_overflow() { new_test_ext().execute_with(|| { // Set the count to the largest value possible. CountForKitties::<TestRuntime>::set(u32::MAX); // `create_kitty` should not succeed because of safe math. assert_noop!( PalletKitties::create_kitty(RuntimeOrigin::signed(1)), Error::<TestRuntime>::TooManyKitties ); }) } #[test] fn kitties_map_created_correctly() { new_test_ext().execute_with(|| { let zero_key = [0u8; 32]; assert!(!Kitties::<TestRuntime>::contains_key(zero_key)); Kitties::<TestRuntime>::insert(zero_key, DEFAULT_KITTY); assert!(Kitties::<TestRuntime>::contains_key(zero_key)); }) } #[test] fn create_kitty_adds_to_map() { new_test_ext().execute_with(|| { assert_ok!(PalletKitties::create_kitty(RuntimeOrigin::signed(ALICE))); assert_eq!(Kitties::<TestRuntime>::iter().count(), 1); }) } #[test] fn cannot_mint_duplicate_kitty() { new_test_ext().execute_with(|| { assert_ok!(PalletKitties::mint(ALICE, [0u8; 32])); assert_noop!(PalletKitties::mint(BOB, [0u8; 32]), Error::<TestRuntime>::DuplicateKitty); }) } #[test] fn kitty_struct_has_expected_traits() { new_test_ext().execute_with(|| { let kitty = DEFAULT_KITTY; let bytes = kitty.encode(); let _decoded_kitty = Kitty::<TestRuntime>::decode(&mut &bytes[..]).unwrap(); assert!(Kitty::<TestRuntime>::max_encoded_len() > 0); let _info = Kitty::<TestRuntime>::type_info(); }) } #[test] fn mint_stores_owner_in_kitty() { new_test_ext().execute_with(|| { assert_ok!(PalletKitties::mint(1337, [42u8; 32])); let kitty = Kitties::<TestRuntime>::get([42u8; 32]).unwrap(); assert_eq!(kitty.owner, 1337); assert_eq!(kitty.dna, [42u8; 32]); }) } #[test] fn create_kitty_makes_unique_kitties() { new_test_ext().execute_with(|| { // Two calls to `create_kitty` should work. assert_ok!(PalletKitties::create_kitty(RuntimeOrigin::signed(ALICE))); assert_ok!(PalletKitties::create_kitty(RuntimeOrigin::signed(BOB))); // And should result in two kitties in our system. assert_eq!(CountForKitties::<TestRuntime>::get(), 2); assert_eq!(Kitties::<TestRuntime>::iter().count(), 2); }) } #[test] fn kitties_owned_created_correctly() { new_test_ext().execute_with(|| { // Initially users have no kitties owned. assert_eq!(KittiesOwned::<TestRuntime>::get(1).len(), 0); // Let's create two kitties. assert_ok!(PalletKitties::create_kitty(RuntimeOrigin::signed(ALICE))); assert_ok!(PalletKitties::create_kitty(RuntimeOrigin::signed(ALICE))); // Now they should have two kitties owned. assert_eq!(KittiesOwned::<TestRuntime>::get(1).len(), 2); }); } #[test] fn cannot_own_too_many_kitties() { new_test_ext().execute_with(|| { // If your max owned is different than 100, you will need to update this. for _ in 0..100 { assert_ok!(PalletKitties::create_kitty(RuntimeOrigin::signed(ALICE))); } assert_noop!( PalletKitties::create_kitty(RuntimeOrigin::signed(1)), Error::<TestRuntime>::TooManyOwned ); }); } #[test] fn transfer_emits_event() { new_test_ext().execute_with(|| { // We need to set block number to 1 to view events. System::set_block_number(1); // Create a kitty to transfer assert_ok!(PalletKitties::create_kitty(RuntimeOrigin::signed(ALICE))); // Get the kitty id. let kitty_id = Kitties::<TestRuntime>::iter_keys().collect::<Vec<_>>()[0]; assert_ok!(PalletKitties::transfer(RuntimeOrigin::signed(ALICE), BOB, kitty_id)); System::assert_last_event( Event::<TestRuntime>::Transferred { from: ALICE, to: BOB, kitty_id }.into(), ); }); } #[test] fn transfer_logic_works() { new_test_ext().execute_with(|| { assert_ok!(PalletKitties::create_kitty(RuntimeOrigin::signed(ALICE))); // Starting state looks good. let kitty = &Kitties::<TestRuntime>::iter_values().collect::<Vec<_>>()[0]; let kitty_id = kitty.dna; assert_eq!(kitty.owner, ALICE); assert_eq!(KittiesOwned::<TestRuntime>::get(ALICE), vec![kitty_id]); assert_eq!(KittiesOwned::<TestRuntime>::get(BOB), vec![]); // Cannot transfer to yourself. assert_noop!( PalletKitties::transfer(RuntimeOrigin::signed(ALICE), ALICE, kitty_id), Error::<TestRuntime>::TransferToSelf ); // Cannot transfer a non-existent kitty. assert_noop!( PalletKitties::transfer(RuntimeOrigin::signed(ALICE), BOB, [0u8; 32]), Error::<TestRuntime>::NoKitty ); // Cannot transfer kitty you do not own. assert_noop!( PalletKitties::transfer(RuntimeOrigin::signed(BOB), ALICE, kitty_id), Error::<TestRuntime>::NotOwner ); // Transfer should work when parameters are right. assert_ok!(PalletKitties::transfer(RuntimeOrigin::signed(ALICE), BOB, kitty_id)); // Storage is updated correctly. assert_eq!(KittiesOwned::<TestRuntime>::get(ALICE), vec![]); assert_eq!(KittiesOwned::<TestRuntime>::get(BOB), vec![kitty_id]); let kitty = &Kitties::<TestRuntime>::iter_values().collect::<Vec<_>>()[0]; assert_eq!(kitty.owner, BOB); }); } #[test] fn native_balance_associated_type_works() { new_test_ext().execute_with(|| { assert_ok!(<<TestRuntime as Config>::NativeBalance as Mutate<_>>::mint_into(&ALICE, 1337)); assert_eq!( <<TestRuntime as Config>::NativeBalance as Inspect<_>>::total_balance(&ALICE), 1337 ); }); } #[test] fn balance_of_type_works() { // Inside our tests, the `BalanceOf` type has a concrete type of `u64`. let _example_balance: BalanceOf<TestRuntime> = 1337u64; } #[test] fn set_price_emits_event() { new_test_ext().execute_with(|| { // We need to set block number to 1 to view events. System::set_block_number(1); assert_ok!(PalletKitties::create_kitty(RuntimeOrigin::signed(ALICE))); let kitty_id = Kitties::<TestRuntime>::iter_keys().collect::<Vec<_>>()[0]; assert_ok!(PalletKitties::set_price(RuntimeOrigin::signed(ALICE), kitty_id, Some(1337))); // Assert the last event is `PriceSet` event with the correct information. System::assert_last_event( Event::<TestRuntime>::PriceSet { owner: ALICE, kitty_id, new_price: Some(1337) }.into(), ); }) } }
diff --git a/src/impls.rs b/src/impls.rs
index 16bdb73..f3f8281 100644
--- a/src/impls.rs
+++ b/src/impls.rs
@@ -58,4 +58,15 @@ impl<T: Config> Pallet<T> {
Self::deposit_event(Event::<T>::Transferred { from, to, kitty_id });
Ok(())
}
+
+ /* 🚧 TODO 🚧: Make an internal function called `do_set_price`:
+ - Inputs to the function are:
+ - `caller` which is `T::AccountId`.
+ - `kitty_id` which is `[u8; 32]`.
+ - `new_price` which is `Option<BalanceOf<T>`.
+ - Returns a `DispatchResult`.
+ - The internal logic, for now, should be:
+ - `Self::deposit_event` for `Event::<T>::PriceSet` with the appropriate params.
+ - Return `Ok(())`.
+ */
}
diff --git a/src/lib.rs b/src/lib.rs
index 7d8350d..45f5fdf 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -55,6 +55,11 @@ pub mod pallet {
pub enum Event<T: Config> {
Created { owner: T::AccountId },
Transferred { from: T::AccountId, to: T::AccountId, kitty_id: [u8; 32] },
+ /* 🚧 TODO 🚧: Create a new `Event` called `PriceSet` with fields:
+ - `owner` which is `T::AccountId`.
+ - `kitty_id` which is `[u8; 32]`.
+ - `new_price` which is `Option<BalanceOf<T>>`.
+ */
}
#[pallet::error]
@@ -85,5 +90,17 @@ pub mod pallet {
Self::do_transfer(who, to, kitty_id)?;
Ok(())
}
+
+ /* 🚧 TODO 🚧: Make an callable function called `set_price`:
+ - Inputs to the function are:
+ - `origin` which is `OriginFor<T>`.
+ - `kitty_id` which is `[u8; 32]`.
+ - `new_price` which is `Option<BalanceOf<T>`.
+ - Returns a `DispatchResult`
+ - The internal logic, for now, should be:
+ - Extract the caller `who` with `ensure_signed`.
+ - Call `Self::do_set_price` with the appropriate parameters, propagating the result.
+ - Return `Ok(())`.
+ */
}
}
diff --git a/src/impls.rs b/src/impls.rs
index f3f8281..48af847 100644
--- a/src/impls.rs
+++ b/src/impls.rs
@@ -59,14 +59,12 @@ impl<T: Config> Pallet<T> {
Ok(())
}
- /* 🚧 TODO 🚧: Make an internal function called `do_set_price`:
- - Inputs to the function are:
- - `caller` which is `T::AccountId`.
- - `kitty_id` which is `[u8; 32]`.
- - `new_price` which is `Option<BalanceOf<T>`.
- - Returns a `DispatchResult`.
- - The internal logic, for now, should be:
- - `Self::deposit_event` for `Event::<T>::PriceSet` with the appropriate params.
- - Return `Ok(())`.
- */
+ pub fn do_set_price(
+ caller: T::AccountId,
+ kitty_id: [u8; 32],
+ new_price: Option<BalanceOf<T>>,
+ ) -> DispatchResult {
+ Self::deposit_event(Event::<T>::PriceSet { owner: caller, kitty_id, new_price });
+ Ok(())
+ }
}
diff --git a/src/lib.rs b/src/lib.rs
index 45f5fdf..57e0eeb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -55,11 +55,7 @@ pub mod pallet {
pub enum Event<T: Config> {
Created { owner: T::AccountId },
Transferred { from: T::AccountId, to: T::AccountId, kitty_id: [u8; 32] },
- /* 🚧 TODO 🚧: Create a new `Event` called `PriceSet` with fields:
- - `owner` which is `T::AccountId`.
- - `kitty_id` which is `[u8; 32]`.
- - `new_price` which is `Option<BalanceOf<T>>`.
- */
+ PriceSet { owner: T::AccountId, kitty_id: [u8; 32], new_price: Option<BalanceOf<T>> },
}
#[pallet::error]
@@ -91,16 +87,14 @@ pub mod pallet {
Ok(())
}
- /* 🚧 TODO 🚧: Make an callable function called `set_price`:
- - Inputs to the function are:
- - `origin` which is `OriginFor<T>`.
- - `kitty_id` which is `[u8; 32]`.
- - `new_price` which is `Option<BalanceOf<T>`.
- - Returns a `DispatchResult`
- - The internal logic, for now, should be:
- - Extract the caller `who` with `ensure_signed`.
- - Call `Self::do_set_price` with the appropriate parameters, propagating the result.
- - Return `Ok(())`.
- */
+ pub fn set_price(
+ origin: OriginFor<T>,
+ kitty_id: [u8; 32],
+ new_price: Option<BalanceOf<T>>,
+ ) -> DispatchResult {
+ let who = ensure_signed(origin)?;
+ Self::do_set_price(who, kitty_id, new_price)?;
+ Ok(())
+ }
}
}
diff --git a/src/tests.rs b/src/tests.rs
index 15c7df4..b71e968 100644
--- a/src/tests.rs
+++ b/src/tests.rs
@@ -310,3 +310,18 @@ fn balance_of_type_works() {
// Inside our tests, the `BalanceOf` type has a concrete type of `u64`.
let _example_balance: BalanceOf<TestRuntime> = 1337u64;
}
+
+#[test]
+fn set_price_emits_event() {
+ new_test_ext().execute_with(|| {
+ // We need to set block number to 1 to view events.
+ System::set_block_number(1);
+ assert_ok!(PalletKitties::create_kitty(RuntimeOrigin::signed(ALICE)));
+ let kitty_id = Kitties::<TestRuntime>::iter_keys().collect::<Vec<_>>()[0];
+ assert_ok!(PalletKitties::set_price(RuntimeOrigin::signed(ALICE), kitty_id, Some(1337)));
+ // Assert the last event is `PriceSet` event with the correct information.
+ System::assert_last_event(
+ Event::<TestRuntime>::PriceSet { owner: ALICE, kitty_id, new_price: Some(1337) }.into(),
+ );
+ })
+}