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.
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 the PriceSet event.
We will actually populate the logic for do_set_price in the next step.
#![allow(unused)]fnmain() {
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 DNApubfngen_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()
}
pubfnmint(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(())
}
pubfndo_transfer(from: T::AccountId, to: T::AccountId, kitty_id: [u8; 32]) -> DispatchResult {
ensure!(from != to, Error::<T>::TransferToSelf);
letmut 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;
letmut to_owned = KittiesOwned::<T>::get(&to);
to_owned.try_push(kitty_id).map_err(|_| Error::<T>::TooManyOwned)?;
letmut from_owned = KittiesOwned::<T>::get(&from);
ifletSome(ind) = from_owned.iter().position(|&id| id == kitty_id) {
from_owned.swap_remove(ind);
} else {
returnErr(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)]fnmain() {
mod impls;
mod tests;
use frame::prelude::*;
use frame::traits::fungible::Inspect;
use frame::traits::fungible::Mutate;
pubuse pallet::*;
#[frame::pallet(dev_mode)]pubmod pallet {
use super::*;
#[pallet::pallet]pubstructPallet<T>(core::marker::PhantomData<T>);
#[pallet::config]pubtraitConfig: frame_system::Config {
typeRuntimeEvent: From<Event<Self>> + IsType<<Selfas frame_system::Config>::RuntimeEvent>;
/// The Fungible handler for the kitties pallet.typeNativeBalance: Inspect<Self::AccountId> + Mutate<Self::AccountId>;
}
// Allows easy access our Pallet's `Balance` type. Comes from `Fungible` interface.pubtypeBalanceOf<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))]pubstructKitty<T: Config> {
// Using 32 bytes to represent a kitty DNApub dna: [u8; 32],
pub owner: T::AccountId,
pub price: Option<BalanceOf<T>>,
}
#[pallet::storage]pub(super) typeCountForKitties<T: Config> = StorageValue<Value = u32, QueryKind = ValueQuery>;
#[pallet::storage]pub(super) typeKitties<T: Config> = StorageMap<Key = [u8; 32], Value = Kitty<T>>;
/// Track the kitties owned by each account.#[pallet::storage]pub(super) typeKittiesOwned<T: Config> = StorageMap<
Key = T::AccountId,
Value = BoundedVec<[u8; 32], ConstU32<100>>,
QueryKind = ValueQuery,
>;
#[pallet::event]#[pallet::generate_deposit(pub(super) fn deposit_event)]pubenumEvent<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]pubenumError<T> {
TooManyKitties,
DuplicateKitty,
TooManyOwned,
TransferToSelf,
NoKitty,
NotOwner,
}
#[pallet::call]impl<T: Config> Pallet<T> {
pubfncreate_kitty(origin: OriginFor<T>) -> DispatchResult {
let who = ensure_signed(origin)?;
let dna = Self::gen_dna();
Self::mint(who, dna)?;
Ok(())
}
pubfntransfer(
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)]fnmain() {
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 DNApubfngen_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()
}
pubfnmint(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(())
}
pubfndo_transfer(from: T::AccountId, to: T::AccountId, kitty_id: [u8; 32]) -> DispatchResult {
ensure!(from != to, Error::<T>::TransferToSelf);
letmut 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;
letmut to_owned = KittiesOwned::<T>::get(&to);
to_owned.try_push(kitty_id).map_err(|_| Error::<T>::TooManyOwned)?;
letmut from_owned = KittiesOwned::<T>::get(&from);
ifletSome(ind) = from_owned.iter().position(|&id| id == kitty_id) {
from_owned.swap_remove(ind);
} else {
returnErr(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(())
}
pubfndo_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)]fnmain() {
mod impls;
mod tests;
use frame::prelude::*;
use frame::traits::fungible::Inspect;
use frame::traits::fungible::Mutate;
pubuse pallet::*;
#[frame::pallet(dev_mode)]pubmod pallet {
use super::*;
#[pallet::pallet]pubstructPallet<T>(core::marker::PhantomData<T>);
#[pallet::config]pubtraitConfig: frame_system::Config {
typeRuntimeEvent: From<Event<Self>> + IsType<<Selfas frame_system::Config>::RuntimeEvent>;
/// The Fungible handler for the kitties pallet.typeNativeBalance: Inspect<Self::AccountId> + Mutate<Self::AccountId>;
}
// Allows easy access our Pallet's `Balance` type. Comes from `Fungible` interface.pubtypeBalanceOf<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))]pubstructKitty<T: Config> {
// Using 32 bytes to represent a kitty DNApub dna: [u8; 32],
pub owner: T::AccountId,
pub price: Option<BalanceOf<T>>,
}
#[pallet::storage]pub(super) typeCountForKitties<T: Config> = StorageValue<Value = u32, QueryKind = ValueQuery>;
#[pallet::storage]pub(super) typeKitties<T: Config> = StorageMap<Key = [u8; 32], Value = Kitty<T>>;
/// Track the kitties owned by each account.#[pallet::storage]pub(super) typeKittiesOwned<T: Config> = StorageMap<
Key = T::AccountId,
Value = BoundedVec<[u8; 32], ConstU32<100>>,
QueryKind = ValueQuery,
>;
#[pallet::event]#[pallet::generate_deposit(pub(super) fn deposit_event)]pubenumEvent<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]pubenumError<T> {
TooManyKitties,
DuplicateKitty,
TooManyOwned,
TransferToSelf,
NoKitty,
NotOwner,
}
#[pallet::call]impl<T: Config> Pallet<T> {
pubfncreate_kitty(origin: OriginFor<T>) -> DispatchResult {
let who = ensure_signed(origin)?;
let dna = Self::gen_dna();
Self::mint(who, dna)?;
Ok(())
}
pubfntransfer(
origin: OriginFor<T>,
to: T::AccountId,
kitty_id: [u8; 32],
) -> DispatchResult {
let who = ensure_signed(origin)?;
Self::do_transfer(who, to, kitty_id)?;
Ok(())
}
pubfnset_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)]fnmain() {
// 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)]usecrateas pallet_kitties;
use crate::*;
use frame::deps::frame_support::runtime;
use frame::deps::sp_io;
use frame::runtime::prelude::*;
use frame::testing_prelude::*;
use frame::traits::fungible::*;
typeBalance = u64;
typeBlock = 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 };
#[runtime]mod runtime {
#[runtime::derive(
RuntimeCall,
RuntimeEvent,
RuntimeError,
RuntimeOrigin,
RuntimeTask,
RuntimeHoldReason,
RuntimeFreezeReason
)]#[runtime::runtime]/// The "test runtime" that represents the state transition function for our blockchain.////// The runtime is composed of individual modules called "pallets", which you find see below./// Each pallet has its own logic and storage, all of which can be combined together.pubstructTestRuntime;
/// System: Mandatory system pallet that should always be included in a FRAME runtime.#[runtime::pallet_index(0)]pubtypeSystem = frame_system::Pallet<TestRuntime>;
/// PalletBalances: Manages your blockchain's native currency. (i.e. DOT on Polkadot)#[runtime::pallet_index(1)]pubtypePalletBalances = pallet_balances::Pallet<TestRuntime>;
/// PalletKitties: The pallet you are building in this tutorial!#[runtime::pallet_index(2)]pubtypePalletKitties = pallet_kitties::Pallet<TestRuntime>;
}
// 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 {
typeBlock = Block;
typeAccountData = 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 {
typeAccountStore = System;
typeBalance = 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 {
typeRuntimeEvent = RuntimeEvent;
typeNativeBalance = 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."pubfnnew_test_ext() -> sp_io::TestExternalities {
frame_system::GenesisConfig::<TestRuntime>::default()
.build_storage()
.unwrap()
.into()
}
#[test]fnstarting_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]fnsystem_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 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]fncreate_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]fncreate_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]fncount_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]fnmint_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]fnmint_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]fnkitties_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]fncreate_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]fncannot_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]fnkitty_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]fnmint_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]fncreate_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]fnkitties_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]fncannot_own_too_many_kitties() {
new_test_ext().execute_with(|| {
// If your max owned is different than 100, you will need to update this.for _ in0..100 {
assert_ok!(PalletKitties::create_kitty(RuntimeOrigin::signed(ALICE)));
}
assert_noop!(
PalletKitties::create_kitty(RuntimeOrigin::signed(1)),
Error::<TestRuntime>::TooManyOwned
);
});
}
#[test]fntransfer_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]fntransfer_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]fnnative_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]fnbalance_of_type_works() {
// Inside our tests, the `BalanceOf` type has a concrete type of `u64`.let _example_balance: BalanceOf<TestRuntime> = 1337u64;
}
#[test]fnset_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 16bdb731..f3f82812 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 7d8350d6..45f5fdfb 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 f3f82812..48af8471 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 45f5fdfb..57e0eeb7 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 ff0b8c24..e47a4736 100644
--- a/src/tests.rs+++ b/src/tests.rs
@@ -328,3 +328,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(),+ );+ })+}