-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkitties.src.lib.rs
389 lines (320 loc) · 12.1 KB
/
kitties.src.lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
#![cfg_attr(not(feature = "std"), no_std)]
pub use pallet::*;
#[frame_support::pallet]
pub mod pallet {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use frame_support::{
sp_runtime::traits::Hash,
traits::{ Randomness, Currency, tokens::ExistenceRequirement },
transactional
};
use sp_io::hashing::blake2_128;
use scale_info::TypeInfo;
#[cfg(feature = "std")]
use frame_support::serde::{Deserialize, Serialize};
type AccountOf<T> = <T as frame_system::Config>::AccountId;
type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
// Struct for holding Kitty information.
#[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
#[scale_info(skip_type_params(T))]
#[codec(mel_bound())]
pub struct Kitty<T: Config> {
pub dna: [u8; 16], // Using 16 bytes to represent a kitty DNA
pub price: Option<BalanceOf<T>>,
pub gender: Gender,
pub owner: AccountOf<T>,
}
// Set Gender type in Kitty struct.
#[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub enum Gender {
Male,
Female,
}
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
// Configure the pallet by specifying the parameters and types on which it depends.
#[pallet::config]
pub trait Config: frame_system::Config {
/// Because this pallet emits events, it depends on the runtime's definition of an event.
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
/// The Currency handler for the Kitties pallet.
type Currency: Currency<Self::AccountId>;
/// The maximum amount of Kitties a single account can own.
#[pallet::constant]
type MaxKittyOwned: Get<u32>;
/// The type of Randomness we want to specify for this pallet.
type KittyRandomness: Randomness<Self::Hash, Self::BlockNumber>;
}
// Errors.
#[pallet::error]
pub enum Error<T> {
/// Handles arithemtic overflow when incrementing the Kitty counter.
CountForKittiesOverflow,
/// An account cannot own more Kitties than `MaxKittyCount`.
ExceedMaxKittyOwned,
/// Buyer cannot be the owner.
BuyerIsKittyOwner,
/// Cannot transfer a kitty to its owner.
TransferToSelf,
/// This kitty already exists
KittyExists,
/// Handles checking whether the Kitty exists.
KittyNotExist,
/// Handles checking that the Kitty is owned by the account transferring, buying or setting a price for it.
NotKittyOwner,
/// Ensures the Kitty is for sale.
KittyNotForSale,
/// Ensures that the buying price is greater than the asking price.
KittyBidPriceTooLow,
/// Ensures that an account has enough funds to purchase a Kitty.
NotEnoughBalance,
}
// Events.
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// A new Kitty was successfully created. \[sender, kitty_id\]
Created(T::AccountId, T::Hash),
/// Kitty price was successfully set. \[sender, kitty_id, new_price\]
PriceSet(T::AccountId, T::Hash, Option<BalanceOf<T>>),
/// A Kitty was successfully transferred. \[from, to, kitty_id\]
Transferred(T::AccountId, T::AccountId, T::Hash),
/// A Kitty was successfully bought. \[buyer, seller, kitty_id, bid_price\]
Bought(T::AccountId, T::AccountId, T::Hash, BalanceOf<T>),
}
// Storage items.
#[pallet::storage]
#[pallet::getter(fn count_for_kitties)]
/// Keeps track of the number of Kitties in existence.
pub(super) type CountForKitties<T: Config> = StorageValue<_, u64, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn kitties)]
/// Stores a Kitty's unique traits, owner and price.
pub(super) type Kitties<T: Config> = StorageMap<_, Twox64Concat, T::Hash, Kitty<T>>;
#[pallet::storage]
#[pallet::getter(fn kitties_owned)]
/// Keeps track of what accounts own what Kitty.
pub(super) type KittiesOwned<T: Config> =
StorageMap<_, Twox64Concat, T::AccountId, BoundedVec<T::Hash, T::MaxKittyOwned>, ValueQuery>;
// ACTION #10: Our pallet's genesis configuration.
// Our pallet's genesis configuration.
#[pallet::genesis_config]
pub struct GenesisConfig<T: Config> {
pub kitties: Vec<(T::AccountId, [u8; 16], Gender)>,
}
// Required to implement default for GenesisConfig.
#[cfg(feature = "std")]
impl<T: Config> Default for GenesisConfig<T> {
fn default() -> GenesisConfig<T> {
GenesisConfig { kitties: vec![] }
}
}
#[pallet::genesis_build]
impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
fn build(&self) {
// When building a kitty from genesis config, we require the dna and gender to be supplied.
for (acct, dna, gender) in &self.kitties {
let _ = <Pallet<T>>::mint(acct, Some(dna.clone()), Some(gender.clone()));
}
}
}
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Create a new unique kitty.
///
/// The actual kitty creation is done in the `mint()` function.
#[pallet::weight(100)]
pub fn create_kitty(origin: OriginFor<T>) -> DispatchResult {
let sender = ensure_signed(origin)?;
let kitty_id = Self::mint(&sender, None, None)?;
log::info!("A kitty is born with ID: {:?}.", kitty_id);
Self::deposit_event(Event::Created(sender, kitty_id));
Ok(())
}
/// Set the price for a Kitty.
///
/// Updates Kitty price and updates storage.
#[pallet::weight(100)]
pub fn set_price(
origin: OriginFor<T>,
kitty_id: T::Hash,
new_price: Option<BalanceOf<T>>
) -> DispatchResult {
let sender = ensure_signed(origin)?;
// ACTION #1a: Checking Kitty owner
ensure!(Self::is_kitty_owner(&kitty_id, &sender)?, <Error<T>>::NotKittyOwner);
let mut kitty = Self::kitties(&kitty_id).ok_or(<Error<T>>::KittyNotExist)?;
// ACTION #2: Set the Kitty price and update new Kitty infomation to storage.
kitty.price = new_price.clone();
<Kitties<T>>::insert(&kitty_id, kitty);
// ACTION #3: Deposit a "PriceSet" event.
// Deposit a "PriceSet" event.
Self::deposit_event(Event::PriceSet(sender, kitty_id, new_price));
Ok(())
}
// ACTION #4: transfer
#[pallet::weight(100)]
pub fn transfer(
origin: OriginFor<T>,
to: T::AccountId,
kitty_id: T::Hash
) -> DispatchResult {
let from = ensure_signed(origin)?;
// Ensure the kitty exists and is called by the kitty owner
ensure!(Self::is_kitty_owner(&kitty_id, &from)?, <Error<T>>::NotKittyOwner);
// Verify the kitty is not transferring back to its owner.
ensure!(from != to, <Error<T>>::TransferToSelf);
// Verify the recipient has the capacity to receive one more kitty
let to_owned = <KittiesOwned<T>>::get(&to);
ensure!((to_owned.len() as u32) < T::MaxKittyOwned::get(), <Error<T>>::ExceedMaxKittyOwned);
Self::transfer_kitty_to(&kitty_id, &to)?;
Self::deposit_event(Event::Transferred(from, to, kitty_id));
Ok(())
}
// buy_kitty
#[transactional]
#[pallet::weight(100)]
pub fn buy_kitty(
origin: OriginFor<T>,
kitty_id: T::Hash,
bid_price: BalanceOf<T>
) -> DispatchResult {
let buyer = ensure_signed(origin)?;
// Check the kitty exists and buyer is not the current kitty owner
let kitty = Self::kitties(&kitty_id).ok_or(<Error<T>>::KittyNotExist)?;
ensure!(kitty.owner != buyer, <Error<T>>::BuyerIsKittyOwner);
// ACTION #6: Check if the Kitty is for sale.
// Check the kitty is for sale and the kitty ask price <= bid_price
if let Some(ask_price) = kitty.price {
ensure!(ask_price <= bid_price, <Error<T>>::KittyBidPriceTooLow);
} else {
Err(<Error<T>>::KittyNotForSale)?;
}
// Check the buyer has enough free balance
ensure!(T::Currency::free_balance(&buyer) >= bid_price, <Error<T>>::NotEnoughBalance);
// ACTION #7: Check if buyer can receive Kitty.
// Verify the buyer has the capacity to receive one more kitty
let to_owned = <KittiesOwned<T>>::get(&buyer);
ensure!((to_owned.len() as u32) < T::MaxKittyOwned::get(), <Error<T>>::ExceedMaxKittyOwned);
let seller = kitty.owner.clone();
// ACTION #8: Update Balances using the Currency trait.
// Transfer the amount from buyer to seller
T::Currency::transfer(&buyer, &seller, bid_price, ExistenceRequirement::KeepAlive)?;
// Transfer the kitty from seller to buyer
Self::transfer_kitty_to(&kitty_id, &buyer)?;
// Deposit relevant Event
Self::deposit_event(Event::Bought(buyer, seller, kitty_id, bid_price));
Ok(())
}
/// Breed a Kitty.
///
/// Breed two kitties to create a new generation
/// of Kitties.
#[pallet::weight(100)]
pub fn breed_kitty(
origin: OriginFor<T>,
parent1: T::Hash,
parent2: T::Hash
) -> DispatchResult {
let sender = ensure_signed(origin)?;
// Check: Verify `sender` owns both kitties (and both kitties exist).
ensure!(Self::is_kitty_owner(&parent1, &sender)?, <Error<T>>::NotKittyOwner);
ensure!(Self::is_kitty_owner(&parent2, &sender)?, <Error<T>>::NotKittyOwner);
// ACTION #9: Breed two Kitties using unique DNA and mint new Kitty
let new_dna = Self::breed_dna(&parent1, &parent2)?;
Self::mint(&sender, Some(new_dna), None)?;
Ok(())
}
}
//** Our helper functions.**//
impl<T: Config> Pallet<T> {
fn gen_gender() -> Gender {
let random = T::KittyRandomness::random(&b"gender"[..]).0;
match random.as_ref()[0] % 2 {
0 => Gender::Male,
_ => Gender::Female,
}
}
fn gen_dna() -> [u8; 16] {
let payload = (
T::KittyRandomness::random(&b"dna"[..]).0,
<frame_system::Pallet<T>>::extrinsic_index().unwrap_or_default(),
<frame_system::Pallet<T>>::block_number(),
);
payload.using_encoded(blake2_128)
}
pub fn breed_dna(parent1: &T::Hash, parent2: &T::Hash) -> Result<[u8; 16], Error<T>> {
let dna1 = Self::kitties(parent1).ok_or(<Error<T>>::KittyNotExist)?.dna;
let dna2 = Self::kitties(parent2).ok_or(<Error<T>>::KittyNotExist)?.dna;
let mut new_dna = Self::gen_dna();
for i in 0..new_dna.len() {
new_dna[i] = (new_dna[i] & dna1[i]) | (!new_dna[i] & dna2[i]);
}
Ok(new_dna)
}
// Helper to mint a Kitty.
pub fn mint(
owner: &T::AccountId,
dna: Option<[u8; 16]>,
gender: Option<Gender>,
) -> Result<T::Hash, Error<T>> {
let kitty = Kitty::<T> {
dna: dna.unwrap_or_else(Self::gen_dna),
price: None,
gender: gender.unwrap_or_else(Self::gen_gender),
owner: owner.clone(),
};
let kitty_id = T::Hashing::hash_of(&kitty);
// Performs this operation first as it may fail
let new_cnt = Self::count_for_kitties().checked_add(1)
.ok_or(<Error<T>>::CountForKittiesOverflow)?;
// Check if the kitty does not already exist in our storage map
ensure!(Self::kitties(&kitty_id) == None, <Error<T>>::KittyExists);
// Performs this operation first because as it may fail
<KittiesOwned<T>>::try_mutate(&owner, |kitty_vec| {
kitty_vec.try_push(kitty_id)
}).map_err(|_| <Error<T>>::ExceedMaxKittyOwned)?;
<Kitties<T>>::insert(kitty_id, kitty);
<CountForKitties<T>>::put(new_cnt);
Ok(kitty_id)
}
// ACTION #1b
pub fn is_kitty_owner(kitty_id: &T::Hash, acct: &T::AccountId) -> Result<bool, Error<T>> {
match Self::kitties(kitty_id) {
Some(kitty) => Ok(kitty.owner == *acct),
None => Err(<Error<T>>::KittyNotExist)
}
}
// ACTION #5: Write transfer_kitty_to
#[transactional]
pub fn transfer_kitty_to(
kitty_id: &T::Hash,
to: &T::AccountId,
) -> Result<(), Error<T>> {
let mut kitty = Self::kitties(&kitty_id).ok_or(<Error<T>>::KittyNotExist)?;
let prev_owner = kitty.owner.clone();
// Remove `kitty_id` from the KittiesOwned vector of `prev_owner`
<KittiesOwned<T>>::try_mutate(&prev_owner, |owned| {
if let Some(ind) = owned.iter().position(|&id| id == *kitty_id) {
owned.swap_remove(ind);
return Ok(());
}
Err(())
}).map_err(|_| <Error<T>>::KittyNotExist)?;
// Update the kitty owner
kitty.owner = to.clone();
// Reset the ask price so the kitty is not for sale until `set_price()` is called
// by the current owner.
kitty.price = None;
<Kitties<T>>::insert(kitty_id, kitty);
<KittiesOwned<T>>::try_mutate(to, |vec| {
vec.try_push(*kitty_id)
}).map_err(|_| <Error<T>>::ExceedMaxKittyOwned)?;
Ok(())
}
}
}