【Substrate 第一行代码】 pallet 中操作 balance

130 阅读2分钟

需求

在存证 pallet 的基础上增加操作主币,在创建存证的时候质押 balance ,交易存证的时候返还,接收者继续质押存证,删除存证时返回。

rust docs 使用

使用自带 pallet 的功能时候可以参考其他的 pallet

pallet_assets中就使用了pallet_balances,就是通过指定前者的currency类型为后者 github.com/paritytech/…

可以通过官网的 rust docs 查询 substrate 自带类的说明

例如我们查询将要用的的 currency 的说明

paritytech.github.io/substrate/m…

我们可以通过 官网的 rust docs 来查询 Currency 的用法,

image.png

将要用到的方法

编写 pallet 代码

#![cfg_attr(not(feature = "std"), no_std)]

/// Edit this file to define custom logic or remove it if it is not needed.
/// Learn more about FRAME and the core library of Substrate FRAME pallets:
/// <https://docs.substrate.io/v3/runtime/frame>
pub use pallet::*;


/// 开始定义功能模块
#[frame_support::pallet]
pub mod pallet {
	use frame_support::{pallet_prelude::*, storage::bounded_vec::BoundedVec, traits::Currency};
	use frame_system::pallet_prelude::*;
	use frame_support::traits::{ReservableCurrency};
        type BalanceOf<T> =
		<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
	#[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>;
		type MaxBytesInHash: Get<u32>;
                type PoeLen: AtLeast32Bit + Copy + Parameter + Default + Bounded + MaxEncodedLen;
                type Currency: ReservableCurrency<Self::AccountId>;
                #[pallet::constant]
                type PeoPrice: Get<BalanceOf<Self>>;
	}

	// Pallets use events to inform users when important changes are made.
	// https://docs.substrate.io/v3/runtime/events-and-errors
	#[pallet::event]
	#[pallet::generate_deposit(pub(super) fn deposit_event)]
	pub enum Event<T: Config> {
        /// Event emitted when a proof has been claimed. [who, claim]
        ClaimCreated(T::AccountId, BoundedVec<T::PoeLen, T::MaxBytesInHash>),
        /// Event emitted when a claim is revoked by the owner. [who, claim]
        ClaimRevoked(T::AccountId, BoundedVec<T::PoeLen, T::MaxBytesInHash>),

		ClaimTransaction(T::AccountId, T::AccountId, BoundedVec<T::PoeLen, T::MaxBytesInHash>),
	}

	// Errors inform users that something went wrong.
	#[pallet::error]
	pub enum Error<T> {
        /// The proof has already been claimed.
        ProofAlreadyClaimed,
        /// The proof does not exist, so it cannot be revoked.
        NoSuchProof,
        /// The proof is claimed by another account, so caller can't revoke it.
        NotProofOwner,
	}

	//可调用函数
	// Dispatchable functions allows users to interact with the pallet and invoke state changes.
	// These functions materialize as "extrinsics", which are often compared to transactions.
	// Dispatchable functions must be annotated with a weight and must return a DispatchResult.
	#[pallet::call]
	impl<T: Config> Pallet<T> {
		#[pallet::weight(1_000)]
		pub fn create_claim(
			origin:OriginFor<T>,
			proof:BoundedVec<T::PoeLen,T::MaxBytesInHash>,
		) -> DispatchResult {
			// 检查是否签名
			let sender = ensure_signed(origin)?;
			//检查 proof 是否已经被 claimed, 是的话 报错 ProofAlreadyClaimed
			let poe_price = T::PeoPrice::get();
                        ensure!(!Proofs::<T>::contains_key(&proof),Error::<T>::ProofAlreadyClaimed);
                        // 质押主币
                        T::Currency::reserve(&who, poe_price)?;
			// 获得当前的块号
			let current_block = <frame_system::Pallet<T>>::block_number();
		// 向 StorageMap 中插入
			Proofs::<T>::insert(&proof, (&sender, current_block));
		// 发送 ClaimCreated 通知
			Self::deposit_event(Event::ClaimCreated(sender, proof));
			Ok(())
		}
		#[pallet::weight(1_000)]
		pub fn revole_claim(
			origin:OriginFor<T>,
			proof:BoundedVec<T::PoeLen,T::MaxBytesInHash>
		)->DispatchResult{
			let sender = ensure_signed(origin)?;
			ensure!(Proofs::<T>::contains_key(&proof), Error::<T>::NoSuchProof);
                        let poe_price = T::PeoPrice::get();
			// StorageMap 获得 value
			let (owner, _) = Proofs::<T>::get(&proof).expect("All proofs must have an owner!");
		
			ensure!(sender == owner, Error::<T>::NotProofOwner);
                         // 释放主币
                        T::Currency::unreserve(&who, poe_price);
			Proofs::<T>::remove(&proof);

			Self::deposit_event(Event::ClaimRevoked(sender, proof));
			Ok(())
		}
		#[pallet::weight(1_000)]
		pub fn transaction_claim(
			origin:OriginFor<T>,
			proof:BoundedVec<T::PoeLen,T::MaxBytesInHash>,
			to_address:T::AccountId
		)->DispatchResult{
			let sender = ensure_signed(origin)?;
                        let poe_price = T::PeoPrice::get();
			ensure!(Proofs::<T>::contains_key(&proof), Error::<T>::NoSuchProof);
			// StorageMap 获得 value
			let (owner, _) = Proofs::<T>::get(&proof).expect("All proofs must have an owner!");
		
			ensure!(sender == owner, Error::<T>::NotProofOwner);
                    
                        // 获得当前的块号
			let current_block = <frame_system::Pallet<T>>::block_number();
			Proofs::<T>::remove(&proof);
		// 向 StorageMap 中插入
			Proofs::<T>::insert(&proof, (&to_address, current_block)); 
			
			Self::deposit_event(Event::ClaimTransaction(sender, to_address,proof));
			Ok(())
		}

	}

	////定义存证单元 可选择get函数
	#[pallet::storage]
	#[pallet::getter(fn proofs)]
	pub(super) type Proofs<T:Config> = StorageMap<
		_,
		Blake2_128Concat,//key加密方式
		BoundedVec<T::PoeLen,T::MaxBytesInHash>,//key
		(T::AccountId,T::BlockNumber),//value
		OptionQuery,//查询方式
	>;
}

修改 runtime

impl pallet_poe::Config for Runtime {
	type Event = Event;
	type MaxBytesInHash = frame_support::traits::ConstU32<64>;
        type PoeLen = u8;
        type Currency = Balances;
        type PeoPrice = ConstU128<512>;
}