referrerpolicy=no-referrer-when-downgrade

polkadot_sdk_docs_first_pallet/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Pallets used in the `your_first_pallet` guide.
19
20#![cfg_attr(not(feature = "std"), no_std)]
21
22#[docify::export]
23#[frame::pallet(dev_mode)]
24pub mod shell_pallet {
25	use frame::prelude::*;
26
27	#[pallet::config]
28	pub trait Config: frame_system::Config {}
29
30	#[pallet::pallet]
31	pub struct Pallet<T>(_);
32}
33
34#[frame::pallet(dev_mode)]
35pub mod pallet {
36	use frame::prelude::*;
37
38	#[docify::export]
39	pub type Balance = u128;
40
41	#[pallet::config]
42	pub trait Config: frame_system::Config {}
43
44	#[pallet::pallet]
45	pub struct Pallet<T>(_);
46
47	#[docify::export]
48	/// Single storage item, of type `Balance`.
49	#[pallet::storage]
50	pub type TotalIssuance<T: Config> = StorageValue<_, Balance>;
51
52	#[docify::export]
53	/// A mapping from `T::AccountId` to `Balance`
54	#[pallet::storage]
55	pub type Balances<T: Config> = StorageMap<_, _, T::AccountId, Balance>;
56
57	#[docify::export(impl_pallet)]
58	#[pallet::call]
59	impl<T: Config> Pallet<T> {
60		/// An unsafe mint that can be called by anyone. Not a great idea.
61		pub fn mint_unsafe(
62			origin: T::RuntimeOrigin,
63			dest: T::AccountId,
64			amount: Balance,
65		) -> DispatchResult {
66			// ensure that this is a signed account, but we don't really check `_anyone`.
67			let _anyone = ensure_signed(origin)?;
68
69			// update the balances map. Notice how all `<T: Config>` remains as `<T>`.
70			Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
71			// update total issuance.
72			TotalIssuance::<T>::mutate(|t| *t = Some(t.unwrap_or(0) + amount));
73
74			Ok(())
75		}
76
77		/// Transfer `amount` from `origin` to `dest`.
78		pub fn transfer(
79			origin: T::RuntimeOrigin,
80			dest: T::AccountId,
81			amount: Balance,
82		) -> DispatchResult {
83			let sender = ensure_signed(origin)?;
84
85			// ensure sender has enough balance, and if so, calculate what is left after `amount`.
86			let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
87			if sender_balance < amount {
88				return Err("InsufficientBalance".into())
89			}
90			let remainder = sender_balance - amount;
91
92			// update sender and dest balances.
93			Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
94			Balances::<T>::insert(&sender, remainder);
95
96			Ok(())
97		}
98	}
99
100	#[allow(unused)]
101	impl<T: Config> Pallet<T> {
102		#[docify::export]
103		pub fn transfer_better(
104			origin: T::RuntimeOrigin,
105			dest: T::AccountId,
106			amount: Balance,
107		) -> DispatchResult {
108			let sender = ensure_signed(origin)?;
109
110			let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
111			ensure!(sender_balance >= amount, "InsufficientBalance");
112			let remainder = sender_balance - amount;
113
114			// .. snip
115			Ok(())
116		}
117
118		#[docify::export]
119		/// Transfer `amount` from `origin` to `dest`.
120		pub fn transfer_better_checked(
121			origin: T::RuntimeOrigin,
122			dest: T::AccountId,
123			amount: Balance,
124		) -> DispatchResult {
125			let sender = ensure_signed(origin)?;
126
127			let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
128			let remainder = sender_balance.checked_sub(amount).ok_or("InsufficientBalance")?;
129
130			// .. snip
131			Ok(())
132		}
133	}
134
135	#[cfg(any(test, doc))]
136	pub(crate) mod tests {
137		use crate::pallet::*;
138
139		#[docify::export(testing_prelude)]
140		use frame::testing_prelude::*;
141
142		pub(crate) const ALICE: u64 = 1;
143		pub(crate) const BOB: u64 = 2;
144		pub(crate) const CHARLIE: u64 = 3;
145
146		#[docify::export]
147		// This runtime is only used for testing, so it should be somewhere like `#[cfg(test)] mod
148		// tests { .. }`
149		mod runtime {
150			use super::*;
151			// we need to reference our `mod pallet` as an identifier to pass to
152			// `construct_runtime`.
153			// YOU HAVE TO CHANGE THIS LINE BASED ON YOUR TEMPLATE
154			use crate::pallet as pallet_currency;
155
156			construct_runtime!(
157				pub enum Runtime {
158					// ---^^^^^^ This is where `enum Runtime` is defined.
159					System: frame_system,
160					Currency: pallet_currency,
161				}
162			);
163
164			#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
165			impl frame_system::Config for Runtime {
166				type Block = MockBlock<Runtime>;
167				// within pallet we just said `<T as frame_system::Config>::AccountId`, now we
168				// finally specified it.
169				type AccountId = u64;
170			}
171
172			// our simple pallet has nothing to be configured.
173			impl pallet_currency::Config for Runtime {}
174		}
175
176		pub(crate) use runtime::*;
177
178		#[allow(unused)]
179		#[docify::export]
180		fn new_test_state_basic() -> TestState {
181			let mut state = TestState::new_empty();
182			let accounts = vec![(ALICE, 100), (BOB, 100)];
183			state.execute_with(|| {
184				for (who, amount) in &accounts {
185					Balances::<Runtime>::insert(who, amount);
186					TotalIssuance::<Runtime>::mutate(|b| *b = Some(b.unwrap_or(0) + amount));
187				}
188			});
189
190			state
191		}
192
193		#[docify::export]
194		pub(crate) struct StateBuilder {
195			balances: Vec<(<Runtime as frame_system::Config>::AccountId, Balance)>,
196		}
197
198		#[docify::export(default_state_builder)]
199		impl Default for StateBuilder {
200			fn default() -> Self {
201				Self { balances: vec![(ALICE, 100), (BOB, 100)] }
202			}
203		}
204
205		#[docify::export(impl_state_builder_add)]
206		impl StateBuilder {
207			fn add_balance(
208				mut self,
209				who: <Runtime as frame_system::Config>::AccountId,
210				amount: Balance,
211			) -> Self {
212				self.balances.push((who, amount));
213				self
214			}
215		}
216
217		#[docify::export(impl_state_builder_build)]
218		impl StateBuilder {
219			pub(crate) fn build_and_execute(self, test: impl FnOnce() -> ()) {
220				let mut ext = TestState::new_empty();
221				ext.execute_with(|| {
222					for (who, amount) in &self.balances {
223						Balances::<Runtime>::insert(who, amount);
224						TotalIssuance::<Runtime>::mutate(|b| *b = Some(b.unwrap_or(0) + amount));
225					}
226				});
227
228				ext.execute_with(test);
229
230				// assertions that must always hold
231				ext.execute_with(|| {
232					assert_eq!(
233						Balances::<Runtime>::iter().map(|(_, x)| x).sum::<u128>(),
234						TotalIssuance::<Runtime>::get().unwrap_or_default()
235					);
236				})
237			}
238		}
239
240		#[docify::export]
241		#[test]
242		fn first_test() {
243			TestState::new_empty().execute_with(|| {
244				// We expect Alice's account to have no funds.
245				assert_eq!(Balances::<Runtime>::get(&ALICE), None);
246				assert_eq!(TotalIssuance::<Runtime>::get(), None);
247
248				// mint some funds into Alice's account.
249				assert_ok!(Pallet::<Runtime>::mint_unsafe(
250					RuntimeOrigin::signed(ALICE),
251					ALICE,
252					100
253				));
254
255				// re-check the above
256				assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
257				assert_eq!(TotalIssuance::<Runtime>::get(), Some(100));
258			})
259		}
260
261		#[docify::export]
262		#[test]
263		fn state_builder_works() {
264			StateBuilder::default().build_and_execute(|| {
265				assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
266				assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
267				assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
268				assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
269			});
270		}
271
272		#[docify::export]
273		#[test]
274		fn state_builder_add_balance() {
275			StateBuilder::default().add_balance(CHARLIE, 42).build_and_execute(|| {
276				assert_eq!(Balances::<Runtime>::get(&CHARLIE), Some(42));
277				assert_eq!(TotalIssuance::<Runtime>::get(), Some(242));
278			})
279		}
280
281		#[test]
282		#[should_panic]
283		fn state_builder_duplicate_genesis_fails() {
284			StateBuilder::default()
285				.add_balance(CHARLIE, 42)
286				.add_balance(CHARLIE, 43)
287				.build_and_execute(|| {
288					assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
289					assert_eq!(TotalIssuance::<Runtime>::get(), Some(242));
290				})
291		}
292
293		#[docify::export]
294		#[test]
295		fn mint_works() {
296			StateBuilder::default().build_and_execute(|| {
297				// given the initial state, when:
298				assert_ok!(Pallet::<Runtime>::mint_unsafe(RuntimeOrigin::signed(ALICE), BOB, 100));
299
300				// then:
301				assert_eq!(Balances::<Runtime>::get(&BOB), Some(200));
302				assert_eq!(TotalIssuance::<Runtime>::get(), Some(300));
303
304				// given:
305				assert_ok!(Pallet::<Runtime>::mint_unsafe(
306					RuntimeOrigin::signed(ALICE),
307					CHARLIE,
308					100
309				));
310
311				// then:
312				assert_eq!(Balances::<Runtime>::get(&CHARLIE), Some(100));
313				assert_eq!(TotalIssuance::<Runtime>::get(), Some(400));
314			});
315		}
316
317		#[docify::export]
318		#[test]
319		fn transfer_works() {
320			StateBuilder::default().build_and_execute(|| {
321				// given the initial state, when:
322				assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(ALICE), BOB, 50));
323
324				// then:
325				assert_eq!(Balances::<Runtime>::get(&ALICE), Some(50));
326				assert_eq!(Balances::<Runtime>::get(&BOB), Some(150));
327				assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
328
329				// when:
330				assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(BOB), ALICE, 50));
331
332				// then:
333				assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
334				assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
335				assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
336			});
337		}
338
339		#[docify::export]
340		#[test]
341		fn transfer_from_non_existent_fails() {
342			StateBuilder::default().build_and_execute(|| {
343				// given the initial state, when:
344				assert_err!(
345					Pallet::<Runtime>::transfer(RuntimeOrigin::signed(CHARLIE), ALICE, 10),
346					"NonExistentAccount"
347				);
348
349				// then nothing has changed.
350				assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
351				assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
352				assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
353				assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
354			});
355		}
356	}
357}
358
359#[frame::pallet(dev_mode)]
360pub mod pallet_v2 {
361	use super::pallet::Balance;
362	use frame::prelude::*;
363
364	#[docify::export(config_v2)]
365	#[pallet::config]
366	pub trait Config: frame_system::Config {
367		/// The overarching event type of the runtime.
368		#[allow(deprecated)]
369		type RuntimeEvent: From<Event<Self>>
370			+ IsType<<Self as frame_system::Config>::RuntimeEvent>
371			+ TryInto<Event<Self>>;
372	}
373
374	#[pallet::pallet]
375	pub struct Pallet<T>(_);
376
377	#[pallet::storage]
378	pub type Balances<T: Config> = StorageMap<_, _, T::AccountId, Balance>;
379
380	#[pallet::storage]
381	pub type TotalIssuance<T: Config> = StorageValue<_, Balance>;
382
383	#[docify::export]
384	#[pallet::error]
385	pub enum Error<T> {
386		/// Account does not exist.
387		NonExistentAccount,
388		/// Account does not have enough balance.
389		InsufficientBalance,
390	}
391
392	#[docify::export]
393	#[pallet::event]
394	#[pallet::generate_deposit(pub(super) fn deposit_event)]
395	pub enum Event<T: Config> {
396		/// A transfer succeeded.
397		Transferred { from: T::AccountId, to: T::AccountId, amount: Balance },
398	}
399
400	#[pallet::call]
401	impl<T: Config> Pallet<T> {
402		#[docify::export(transfer_v2)]
403		pub fn transfer(
404			origin: T::RuntimeOrigin,
405			dest: T::AccountId,
406			amount: Balance,
407		) -> DispatchResult {
408			let sender = ensure_signed(origin)?;
409
410			// ensure sender has enough balance, and if so, calculate what is left after `amount`.
411			let sender_balance =
412				Balances::<T>::get(&sender).ok_or(Error::<T>::NonExistentAccount)?;
413			let remainder =
414				sender_balance.checked_sub(amount).ok_or(Error::<T>::InsufficientBalance)?;
415
416			Balances::<T>::mutate(&dest, |b| *b = Some(b.unwrap_or(0) + amount));
417			Balances::<T>::insert(&sender, remainder);
418
419			Self::deposit_event(Event::<T>::Transferred { from: sender, to: dest, amount });
420
421			Ok(())
422		}
423	}
424
425	#[cfg(any(test, doc))]
426	pub mod tests {
427		use super::{super::pallet::tests::StateBuilder, *};
428		use frame::testing_prelude::*;
429		const ALICE: u64 = 1;
430		const BOB: u64 = 2;
431
432		#[docify::export]
433		pub mod runtime_v2 {
434			use super::*;
435			use crate::pallet_v2 as pallet_currency;
436
437			construct_runtime!(
438				pub enum Runtime {
439					System: frame_system,
440					Currency: pallet_currency,
441				}
442			);
443
444			#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
445			impl frame_system::Config for Runtime {
446				type Block = MockBlock<Runtime>;
447				type AccountId = u64;
448			}
449
450			impl pallet_currency::Config for Runtime {
451				type RuntimeEvent = RuntimeEvent;
452			}
453		}
454
455		pub(crate) use runtime_v2::*;
456
457		#[docify::export(transfer_works_v2)]
458		#[test]
459		fn transfer_works() {
460			StateBuilder::default().build_and_execute(|| {
461				// skip the genesis block, as events are not deposited there and we need them for
462				// the final assertion.
463				System::set_block_number(ALICE);
464
465				// given the initial state, when:
466				assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(ALICE), BOB, 50));
467
468				// then:
469				assert_eq!(Balances::<Runtime>::get(&ALICE), Some(50));
470				assert_eq!(Balances::<Runtime>::get(&BOB), Some(150));
471				assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
472
473				// now we can also check that an event has been deposited:
474				assert_eq!(
475					System::read_events_for_pallet::<Event<Runtime>>(),
476					vec![Event::Transferred { from: ALICE, to: BOB, amount: 50 }]
477				);
478			});
479		}
480	}
481}