mirror of
https://github.com/pezkuwichain/docs.git
synced 2026-04-30 23:17:54 +00:00
docs: add Pezkuwi-SDK documentation
- Technical documentation - SDK guides - Architecture overview - Whitepaper - Contributor guides
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "pezkuwi-sdk-docs-first-pallet"
|
||||
description = "A simple pallet created for the pezkuwi-sdk-docs guides"
|
||||
version = "0.0.0"
|
||||
license = "MIT-0"
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition.workspace = true
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
codec = { workspace = true }
|
||||
docify = { workspace = true }
|
||||
frame = { workspace = true, features = ["runtime"] }
|
||||
scale-info = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = ["codec/std", "frame/std", "scale-info/std"]
|
||||
runtime-benchmarks = ["frame/runtime-benchmarks"]
|
||||
@@ -0,0 +1,481 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Pallets used in the `your_first_pallet` guide.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[docify::export]
|
||||
#[frame::pallet(dev_mode)]
|
||||
pub mod shell_pallet {
|
||||
use frame::prelude::*;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
}
|
||||
|
||||
#[frame::pallet(dev_mode)]
|
||||
pub mod pallet {
|
||||
use frame::prelude::*;
|
||||
|
||||
#[docify::export]
|
||||
pub type Balance = u128;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
#[docify::export]
|
||||
/// Single storage item, of type `Balance`.
|
||||
#[pallet::storage]
|
||||
pub type TotalIssuance<T: Config> = StorageValue<_, Balance>;
|
||||
|
||||
#[docify::export]
|
||||
/// A mapping from `T::AccountId` to `Balance`
|
||||
#[pallet::storage]
|
||||
pub type Balances<T: Config> = StorageMap<_, _, T::AccountId, Balance>;
|
||||
|
||||
#[docify::export(impl_pallet)]
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// An unsafe mint that can be called by anyone. Not a great idea.
|
||||
pub fn mint_unsafe(
|
||||
origin: T::RuntimeOrigin,
|
||||
dest: T::AccountId,
|
||||
amount: Balance,
|
||||
) -> DispatchResult {
|
||||
// ensure that this is a signed account, but we don't really check `_anyone`.
|
||||
let _anyone = ensure_signed(origin)?;
|
||||
|
||||
// update the balances map. Notice how all `<T: Config>` remains as `<T>`.
|
||||
Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
|
||||
// update total issuance.
|
||||
TotalIssuance::<T>::mutate(|t| *t = Some(t.unwrap_or(0) + amount));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Transfer `amount` from `origin` to `dest`.
|
||||
pub fn transfer(
|
||||
origin: T::RuntimeOrigin,
|
||||
dest: T::AccountId,
|
||||
amount: Balance,
|
||||
) -> DispatchResult {
|
||||
let sender = ensure_signed(origin)?;
|
||||
|
||||
// ensure sender has enough balance, and if so, calculate what is left after `amount`.
|
||||
let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
|
||||
if sender_balance < amount {
|
||||
return Err("InsufficientBalance".into());
|
||||
}
|
||||
let remainder = sender_balance - amount;
|
||||
|
||||
// update sender and dest balances.
|
||||
Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
|
||||
Balances::<T>::insert(&sender, remainder);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl<T: Config> Pallet<T> {
|
||||
#[docify::export]
|
||||
pub fn transfer_better(
|
||||
origin: T::RuntimeOrigin,
|
||||
dest: T::AccountId,
|
||||
amount: Balance,
|
||||
) -> DispatchResult {
|
||||
let sender = ensure_signed(origin)?;
|
||||
|
||||
let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
|
||||
ensure!(sender_balance >= amount, "InsufficientBalance");
|
||||
let remainder = sender_balance - amount;
|
||||
|
||||
// .. snip
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
/// Transfer `amount` from `origin` to `dest`.
|
||||
pub fn transfer_better_checked(
|
||||
origin: T::RuntimeOrigin,
|
||||
dest: T::AccountId,
|
||||
amount: Balance,
|
||||
) -> DispatchResult {
|
||||
let sender = ensure_signed(origin)?;
|
||||
|
||||
let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
|
||||
let remainder = sender_balance.checked_sub(amount).ok_or("InsufficientBalance")?;
|
||||
|
||||
// .. snip
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, doc))]
|
||||
pub(crate) mod tests {
|
||||
use crate::pallet::*;
|
||||
|
||||
#[docify::export(testing_prelude)]
|
||||
use frame::testing_prelude::*;
|
||||
|
||||
pub(crate) const ALICE: u64 = 1;
|
||||
pub(crate) const BOB: u64 = 2;
|
||||
pub(crate) const CHARLIE: u64 = 3;
|
||||
|
||||
#[docify::export]
|
||||
// This runtime is only used for testing, so it should be somewhere like `#[cfg(test)] mod
|
||||
// tests { .. }`
|
||||
mod runtime {
|
||||
use super::*;
|
||||
// we need to reference our `mod pallet` as an identifier to pass to
|
||||
// `construct_runtime`.
|
||||
// YOU HAVE TO CHANGE THIS LINE BASED ON YOUR TEMPLATE
|
||||
use crate::pallet as pallet_currency;
|
||||
|
||||
construct_runtime!(
|
||||
pub enum Runtime {
|
||||
// ---^^^^^^ This is where `enum Runtime` is defined.
|
||||
System: frame_system,
|
||||
Currency: pallet_currency,
|
||||
}
|
||||
);
|
||||
|
||||
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
|
||||
impl frame_system::Config for Runtime {
|
||||
type Block = MockBlock<Runtime>;
|
||||
// within pallet we just said `<T as frame_system::Config>::AccountId`, now we
|
||||
// finally specified it.
|
||||
type AccountId = u64;
|
||||
}
|
||||
|
||||
// our simple pallet has nothing to be configured.
|
||||
impl pallet_currency::Config for Runtime {}
|
||||
}
|
||||
|
||||
pub(crate) use runtime::*;
|
||||
|
||||
#[allow(unused)]
|
||||
#[docify::export]
|
||||
fn new_test_state_basic() -> TestState {
|
||||
let mut state = TestState::new_empty();
|
||||
let accounts = vec![(ALICE, 100), (BOB, 100)];
|
||||
state.execute_with(|| {
|
||||
for (who, amount) in &accounts {
|
||||
Balances::<Runtime>::insert(who, amount);
|
||||
TotalIssuance::<Runtime>::mutate(|b| *b = Some(b.unwrap_or(0) + amount));
|
||||
}
|
||||
});
|
||||
|
||||
state
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
pub(crate) struct StateBuilder {
|
||||
balances: Vec<(<Runtime as frame_system::Config>::AccountId, Balance)>,
|
||||
}
|
||||
|
||||
#[docify::export(default_state_builder)]
|
||||
impl Default for StateBuilder {
|
||||
fn default() -> Self {
|
||||
Self { balances: vec![(ALICE, 100), (BOB, 100)] }
|
||||
}
|
||||
}
|
||||
|
||||
#[docify::export(impl_state_builder_add)]
|
||||
impl StateBuilder {
|
||||
fn add_balance(
|
||||
mut self,
|
||||
who: <Runtime as frame_system::Config>::AccountId,
|
||||
amount: Balance,
|
||||
) -> Self {
|
||||
self.balances.push((who, amount));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[docify::export(impl_state_builder_build)]
|
||||
impl StateBuilder {
|
||||
pub(crate) fn build_and_execute(self, test: impl FnOnce() -> ()) {
|
||||
let mut ext = TestState::new_empty();
|
||||
ext.execute_with(|| {
|
||||
for (who, amount) in &self.balances {
|
||||
Balances::<Runtime>::insert(who, amount);
|
||||
TotalIssuance::<Runtime>::mutate(|b| *b = Some(b.unwrap_or(0) + amount));
|
||||
}
|
||||
});
|
||||
|
||||
ext.execute_with(test);
|
||||
|
||||
// assertions that must always hold
|
||||
ext.execute_with(|| {
|
||||
assert_eq!(
|
||||
Balances::<Runtime>::iter().map(|(_, x)| x).sum::<u128>(),
|
||||
TotalIssuance::<Runtime>::get().unwrap_or_default()
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
#[test]
|
||||
fn first_test() {
|
||||
TestState::new_empty().execute_with(|| {
|
||||
// We expect Alice's account to have no funds.
|
||||
assert_eq!(Balances::<Runtime>::get(&ALICE), None);
|
||||
assert_eq!(TotalIssuance::<Runtime>::get(), None);
|
||||
|
||||
// mint some funds into Alice's account.
|
||||
assert_ok!(Pallet::<Runtime>::mint_unsafe(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
ALICE,
|
||||
100
|
||||
));
|
||||
|
||||
// re-check the above
|
||||
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(100));
|
||||
})
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
#[test]
|
||||
fn state_builder_works() {
|
||||
StateBuilder::default().build_and_execute(|| {
|
||||
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
||||
assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
|
||||
assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
|
||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
||||
});
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
#[test]
|
||||
fn state_builder_add_balance() {
|
||||
StateBuilder::default().add_balance(CHARLIE, 42).build_and_execute(|| {
|
||||
assert_eq!(Balances::<Runtime>::get(&CHARLIE), Some(42));
|
||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(242));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn state_builder_duplicate_genesis_fails() {
|
||||
StateBuilder::default()
|
||||
.add_balance(CHARLIE, 42)
|
||||
.add_balance(CHARLIE, 43)
|
||||
.build_and_execute(|| {
|
||||
assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
|
||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(242));
|
||||
})
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
#[test]
|
||||
fn mint_works() {
|
||||
StateBuilder::default().build_and_execute(|| {
|
||||
// given the initial state, when:
|
||||
assert_ok!(Pallet::<Runtime>::mint_unsafe(RuntimeOrigin::signed(ALICE), BOB, 100));
|
||||
|
||||
// then:
|
||||
assert_eq!(Balances::<Runtime>::get(&BOB), Some(200));
|
||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(300));
|
||||
|
||||
// given:
|
||||
assert_ok!(Pallet::<Runtime>::mint_unsafe(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
CHARLIE,
|
||||
100
|
||||
));
|
||||
|
||||
// then:
|
||||
assert_eq!(Balances::<Runtime>::get(&CHARLIE), Some(100));
|
||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(400));
|
||||
});
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
#[test]
|
||||
fn transfer_works() {
|
||||
StateBuilder::default().build_and_execute(|| {
|
||||
// given the initial state, when:
|
||||
assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(ALICE), BOB, 50));
|
||||
|
||||
// then:
|
||||
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(50));
|
||||
assert_eq!(Balances::<Runtime>::get(&BOB), Some(150));
|
||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
||||
|
||||
// when:
|
||||
assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(BOB), ALICE, 50));
|
||||
|
||||
// then:
|
||||
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
||||
assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
|
||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
||||
});
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
#[test]
|
||||
fn transfer_from_non_existent_fails() {
|
||||
StateBuilder::default().build_and_execute(|| {
|
||||
// given the initial state, when:
|
||||
assert_err!(
|
||||
Pallet::<Runtime>::transfer(RuntimeOrigin::signed(CHARLIE), ALICE, 10),
|
||||
"NonExistentAccount"
|
||||
);
|
||||
|
||||
// then nothing has changed.
|
||||
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
||||
assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
|
||||
assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
|
||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[frame::pallet(dev_mode)]
|
||||
pub mod pallet_v2 {
|
||||
use super::pallet::Balance;
|
||||
use frame::prelude::*;
|
||||
|
||||
#[docify::export(config_v2)]
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {
|
||||
/// The overarching event type of the runtime.
|
||||
#[allow(deprecated)]
|
||||
type RuntimeEvent: From<Event<Self>>
|
||||
+ IsType<<Self as frame_system::Config>::RuntimeEvent>
|
||||
+ TryInto<Event<Self>>;
|
||||
}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
#[pallet::storage]
|
||||
pub type Balances<T: Config> = StorageMap<_, _, T::AccountId, Balance>;
|
||||
|
||||
#[pallet::storage]
|
||||
pub type TotalIssuance<T: Config> = StorageValue<_, Balance>;
|
||||
|
||||
#[docify::export]
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
/// Account does not exist.
|
||||
NonExistentAccount,
|
||||
/// Account does not have enough balance.
|
||||
InsufficientBalance,
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
#[pallet::event]
|
||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
/// A transfer succeeded.
|
||||
Transferred { from: T::AccountId, to: T::AccountId, amount: Balance },
|
||||
}
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
#[docify::export(transfer_v2)]
|
||||
pub fn transfer(
|
||||
origin: T::RuntimeOrigin,
|
||||
dest: T::AccountId,
|
||||
amount: Balance,
|
||||
) -> DispatchResult {
|
||||
let sender = ensure_signed(origin)?;
|
||||
|
||||
// ensure sender has enough balance, and if so, calculate what is left after `amount`.
|
||||
let sender_balance =
|
||||
Balances::<T>::get(&sender).ok_or(Error::<T>::NonExistentAccount)?;
|
||||
let remainder =
|
||||
sender_balance.checked_sub(amount).ok_or(Error::<T>::InsufficientBalance)?;
|
||||
|
||||
Balances::<T>::mutate(&dest, |b| *b = Some(b.unwrap_or(0) + amount));
|
||||
Balances::<T>::insert(&sender, remainder);
|
||||
|
||||
Self::deposit_event(Event::<T>::Transferred { from: sender, to: dest, amount });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, doc))]
|
||||
pub mod tests {
|
||||
use super::{super::pallet::tests::StateBuilder, *};
|
||||
use frame::testing_prelude::*;
|
||||
const ALICE: u64 = 1;
|
||||
const BOB: u64 = 2;
|
||||
|
||||
#[docify::export]
|
||||
pub mod runtime_v2 {
|
||||
use super::*;
|
||||
use crate::pallet_v2 as pallet_currency;
|
||||
|
||||
construct_runtime!(
|
||||
pub enum Runtime {
|
||||
System: frame_system,
|
||||
Currency: pallet_currency,
|
||||
}
|
||||
);
|
||||
|
||||
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
|
||||
impl frame_system::Config for Runtime {
|
||||
type Block = MockBlock<Runtime>;
|
||||
type AccountId = u64;
|
||||
}
|
||||
|
||||
impl pallet_currency::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use runtime_v2::*;
|
||||
|
||||
#[docify::export(transfer_works_v2)]
|
||||
#[test]
|
||||
fn transfer_works() {
|
||||
StateBuilder::default().build_and_execute(|| {
|
||||
// skip the genesis block, as events are not deposited there and we need them for
|
||||
// the final assertion.
|
||||
System::set_block_number(ALICE);
|
||||
|
||||
// given the initial state, when:
|
||||
assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(ALICE), BOB, 50));
|
||||
|
||||
// then:
|
||||
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(50));
|
||||
assert_eq!(Balances::<Runtime>::get(&BOB), Some(150));
|
||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
||||
|
||||
// now we can also check that an event has been deposited:
|
||||
assert_eq!(
|
||||
System::read_events_for_pallet::<Event<Runtime>>(),
|
||||
vec![Event::Transferred { from: ALICE, to: BOB, amount: 50 }]
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user