Storing a Struct

We have successfully created a generic struct for our pallet. Now we need to actually use it in our runtime.

Derive Macros

One of the powerful tools you get from vanilla Rust is the #[derive(...)] macros.

In the spirit of Rust macros, derive macros help reduce boiler plate code which can be automatically generated for you. In this case, the derive macros generate trait implementations for the objects they are applied on.

The most simple example might be Default.

The verbose way of implementing Default would be:

#![allow(unused)]
fn main() {
pub struct MyObject {
	field_0: u32,
	field_1: u64,
}

impl Default for MyObject {
	fn default() -> Self {
		Self {
			field_0: Default::default(),
			field_1: Default::default(),
		}
	}
}
}

You can see here that we can simply say the default for MyObject is taking the default of each field in MyObject and constructing the struct.

We can do the exact same thing with #[derive(Default)]:

#![allow(unused)]
fn main() {
#[derive(Default)]
pub struct MyObject {
	field_0: u32,
	field_1: u64,
}
}

As long as all the fields inside MyObject implement Default, then the derive macro will handle all the magic.

Remember that T::AccountId explicitly chooses not to implement Default, so you cannot implement Default on the Kitty struct.

Traits Required for Storage

For an object to be placed inside runtime storage, we require it to have a number of traits implemented:

  • Encode: The object must be encodable to bytes using parity_scale_codec.
  • Decode: The object must be decodable from bytes using parity_scale_codec.
  • MaxEncodedLen: When the object is encoded, it must have an upper limit to its size.
  • TypeInfo: The object must be able to generate metadata describing the object.

All of these things are pretty specific to the requirements of using the Polkadot-SDK for building a blockchain.

Parity SCALE Codec

Parity SCALE Codec is a custom encoding and decoding library used in the polkadot-sdk.

The first question we are always asked when talking about SCALE, is why don't we use <your favorite encoder> instead?

Well, SCALE is:

  • Simple to define.
  • Not Rust-specific (but happens to work great in Rust).
    • Easy to derive codec logic: #[derive(Encode, Decode)]
    • Viable and useful for APIs like: MaxEncodedLen and TypeInfo
    • It does not use Rust std, and thus can compile to Wasm no_std.
  • Consensus critical / bijective; one value will always encode to one blob and that blob will only decode to that value.
  • Supports a copy-free decode for basic types on LE architectures (like Wasm).
  • It is about as thin and lightweight as can be.

What you need to know about SCALE is that it defines how every object in the polkadot-sdk is represented in bytes.

Max Encoded Length

Now that we have the tools to define the way objects should be encoded, we are able to create a trait which tracks the maximum encoded length of an object: MaxEncodedLen.

We then use that information to predict in the worst case scenario how much data will be used when we store it.

  • For a u8, the max_encoded_len() is always the same: 1 byte.
  • For a u64, the max_encoded_len() is always the same: 8 bytes.
  • For a basic enum, it is also just 1 byte, since an enum can represent up to 256 variants.
  • For a struct, the max_encoded_len() will be the sum of the max_encoded_len() of all items in the struct.

We need to be able to predict the size of items in our storage because it affects nearly all the constraints of our blockchain: storage size, memory usage, network bandwidth, and even execution time for encoding and decoding.

Type Info

The last required trait for any storage item is TypeInfo.

This trait is key for off-chain interactions with your blockchain. It is used to generate metadata for all the objects and types in your blockchain.

Metadata exposes all the details of your blockchain to the outside world, allowing us to dynamically construct APIs to interact with the blockchain. This is super relevant since the polkadot-sdk is a framework for modular and upgradable blockchains.

We won't really use this in this tutorial, but it is super relevant to learn about once you start getting ready to actually use your blockchain.

Skip Type Params

One nasty thing about the TypeInfo derive macro, is that it isn't very "smart".

As I mentioned, the whole point of TypeInfo is to generate relevant metadata about the types used in your blockchain. However, part of our Kitty type is the generic parameter T, and it really does not make any sense to generate TypeInfo for T.

To make TypeInfo work while we have T, we need to include the additional line:

#![allow(unused)]
fn main() {
#[scale_info(skip_type_params(T))]
}

This tells the TypeInfo derive macro to simply "skip" the T type parameter when generating its code. The best thing for you to do is try compiling your code without this additional line, look at the errors that are generated, then see them disappear with the skip_type_params.

Then in the future, if you run into this error again, you will know what to do.

Your Turn

Now that you know all about the various traits required for runtime development, derive them on the Kitty struct.

Don't forget to include the skip_type_params(T).

After that, update your Kitties map to use Value = Kitty<T>.

Finally, update the logic in mint to create and insert this object into storage.

Learn More

To get a primer on Parity SCALE Codec, check out this video from the Polkadot Blockchain Academy:

#![allow(unused)]
fn main() {
use super::*;
use frame::prelude::*;

impl<T: Config> Pallet<T> {
	pub fn mint(owner: T::AccountId, dna: [u8; 32]) -> DispatchResult {
		/* 🚧 TODO 🚧:
			- Create a new variable `kitty` which is a `Kitty` struct with `dna` and `owner`.
		*/

		// 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)?;
		/* 🚧 TODO 🚧: Insert `kitty`into the map instead of `()`. */
		Kitties::<T>::insert(dna, ());
		CountForKitties::<T>::set(new_count);
		Self::deposit_event(Event::<T>::Created { owner });
		Ok(())
	}
}
}
#![allow(unused)]
#![cfg_attr(not(feature = "std"), no_std)]

fn main() {
mod impls;
mod tests;

use frame::prelude::*;
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>;
	}

	/* 🚧 TODO 🚧:
		- Add the derive macros needed for putting a struct in storage.
		- Add `#[scale_info(skip_type_params(T))]` to ignore the generic `T`.
	*/
	pub struct Kitty<T: Config> {
		// Using 32 bytes to represent a kitty DNA
		pub dna: [u8; 32],
		pub owner: T::AccountId,
	}

	#[pallet::storage]
	pub(super) type CountForKitties<T: Config> = StorageValue<Value = u32, QueryKind = ValueQuery>;

	#[pallet::storage]
	/* 🚧 TODO 🚧: Update the `Value` to be type `Kitty<T>` instead of (). */
	pub(super) type Kitties<T: Config> = StorageMap<Key = [u8; 32], Value = ()>;

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

	#[pallet::error]
	pub enum Error<T> {
		TooManyKitties,
		DuplicateKitty,
	}

	#[pallet::call]
	impl<T: Config> Pallet<T> {
		pub fn create_kitty(origin: OriginFor<T>) -> DispatchResult {
			let who = ensure_signed(origin)?;
			let dna = [0u8; 32];
			Self::mint(who, dna)?;
			Ok(())
		}
	}
}
}
#![allow(unused)]
fn main() {
use super::*;
use frame::prelude::*;

impl<T: Config> Pallet<T> {
	pub fn mint(owner: T::AccountId, dna: [u8; 32]) -> DispatchResult {
		let kitty = Kitty { dna, owner: owner.clone() };
		// 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)?;
		Kitties::<T>::insert(dna, kitty);
		CountForKitties::<T>::set(new_count);
		Self::deposit_event(Event::<T>::Created { owner });
		Ok(())
	}
}
}
#![allow(unused)]
#![cfg_attr(not(feature = "std"), no_std)]

fn main() {
mod impls;
mod tests;

use frame::prelude::*;
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>;
	}

	#[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,
	}

	#[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>>;

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

	#[pallet::error]
	pub enum Error<T> {
		TooManyKitties,
		DuplicateKitty,
	}

	#[pallet::call]
	impl<T: Config> Pallet<T> {
		pub fn create_kitty(origin: OriginFor<T>) -> DispatchResult {
			let who = ensure_signed(origin)?;
			let dna = [0u8; 32];
			Self::mint(who, dna)?;
			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 };

// 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;
}

// 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]);
	})
}
}
diff --git a/src/impls.rs b/src/impls.rs
index c0e7cb2..741a6ff 100644
--- a/src/impls.rs
+++ b/src/impls.rs
@@ -3,11 +3,16 @@ use frame::prelude::*;
 
 impl<T: Config> Pallet<T> {
 	pub fn mint(owner: T::AccountId, dna: [u8; 32]) -> DispatchResult {
+		/* 🚧 TODO 🚧:
+			- Create a new variable `kitty` which is a `Kitty` struct with `dna` and `owner`.
+		*/
+
 		// 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)?;
+		/* 🚧 TODO 🚧: Insert `kitty`into the map instead of `()`. */
 		Kitties::<T>::insert(dna, ());
 		CountForKitties::<T>::set(new_count);
 		Self::deposit_event(Event::<T>::Created { owner });
diff --git a/src/lib.rs b/src/lib.rs
index 30d12b4..68f57e1 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -18,6 +18,10 @@ pub mod pallet {
 		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
 	}
 
+	/* 🚧 TODO 🚧:
+		- Add the derive macros needed for putting a struct in storage.
+		- Add `#[scale_info(skip_type_params(T))]` to ignore the generic `T`.
+	*/
 	pub struct Kitty<T: Config> {
 		// Using 32 bytes to represent a kitty DNA
 		pub dna: [u8; 32],
@@ -28,6 +32,7 @@ pub mod pallet {
 	pub(super) type CountForKitties<T: Config> = StorageValue<Value = u32, QueryKind = ValueQuery>;
 
 	#[pallet::storage]
+	/* 🚧 TODO 🚧: Update the `Value` to be type `Kitty<T>` instead of (). */
 	pub(super) type Kitties<T: Config> = StorageMap<Key = [u8; 32], Value = ()>;
 
 	#[pallet::event]
diff --git a/src/impls.rs b/src/impls.rs
index 741a6ff..5d3c104 100644
--- a/src/impls.rs
+++ b/src/impls.rs
@@ -3,17 +3,13 @@ use frame::prelude::*;
 
 impl<T: Config> Pallet<T> {
 	pub fn mint(owner: T::AccountId, dna: [u8; 32]) -> DispatchResult {
-		/* 🚧 TODO 🚧:
-			- Create a new variable `kitty` which is a `Kitty` struct with `dna` and `owner`.
-		*/
-
+		let kitty = Kitty { dna, owner: owner.clone() };
 		// 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)?;
-		/* 🚧 TODO 🚧: Insert `kitty`into the map instead of `()`. */
-		Kitties::<T>::insert(dna, ());
+		Kitties::<T>::insert(dna, kitty);
 		CountForKitties::<T>::set(new_count);
 		Self::deposit_event(Event::<T>::Created { owner });
 		Ok(())
diff --git a/src/lib.rs b/src/lib.rs
index 68f57e1..aa6ca3b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -18,10 +18,8 @@ pub mod pallet {
 		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
 	}
 
-	/* 🚧 TODO 🚧:
-		- Add the derive macros needed for putting a struct in storage.
-		- Add `#[scale_info(skip_type_params(T))]` to ignore the generic `T`.
-	*/
+	#[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],
@@ -32,8 +30,7 @@ pub mod pallet {
 	pub(super) type CountForKitties<T: Config> = StorageValue<Value = u32, QueryKind = ValueQuery>;
 
 	#[pallet::storage]
-	/* 🚧 TODO 🚧: Update the `Value` to be type `Kitty<T>` instead of (). */
-	pub(super) type Kitties<T: Config> = StorageMap<Key = [u8; 32], Value = ()>;
+	pub(super) type Kitties<T: Config> = StorageMap<Key = [u8; 32], Value = Kitty<T>>;
 
 	#[pallet::event]
 	#[pallet::generate_deposit(pub(super) fn deposit_event)]
diff --git a/src/tests.rs b/src/tests.rs
index 58e2b28..53a248b 100644
--- a/src/tests.rs
+++ b/src/tests.rs
@@ -27,7 +27,6 @@ type Block = frame_system::mocking::MockBlock<TestRuntime>;
 // 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;
-#[allow(unused)]
 const DEFAULT_KITTY: Kitty<TestRuntime> = Kitty { dna: [0u8; 32], owner: 0 };
 
 // Our blockchain tests only need 3 Pallets:
@@ -162,7 +161,7 @@ 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, ());
+		Kitties::<TestRuntime>::insert(zero_key, DEFAULT_KITTY);
 		assert!(Kitties::<TestRuntime>::contains_key(zero_key));
 	})
 }
@@ -182,3 +181,24 @@ fn cannot_mint_duplicate_kitty() {
 		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]);
+	})
+}