Skip to content

Commit

Permalink
Merge pull request #7 from gleb-urvanov/feature/asset-info
Browse files Browse the repository at this point in the history
Feature/asset info
  • Loading branch information
gleb-urvanov authored Nov 24, 2020
2 parents 9e544bc + 2313025 commit 3175b0d
Show file tree
Hide file tree
Showing 8 changed files with 1,337 additions and 673 deletions.
1,599 changes: 928 additions & 671 deletions Cargo.lock

Large diffs are not rendered by default.

37 changes: 37 additions & 0 deletions pallets/assets-info/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[package]
authors = ['Substrate DevHub <https://github.com/substrate-developer-hub>']
description = 'FRAME pallet template for defining custom runtime logic.'
edition = '2018'
homepage = 'https://substrate.dev'
license = 'Unlicense'
name = 'pallet-assets-info'
repository = 'https://github.com/substrate-developer-hub/substrate-node-template/'
version = '2.0.0'

[package.metadata.docs.rs]
targets = ['x86_64-unknown-linux-gnu']

# alias "parity-scale-code" to "codec"
[dependencies.codec]
default-features = false
features = ['derive']
package = 'parity-scale-codec'
version = '1.3.4'

[dependencies]
frame-support = { default-features = false, version = '2.0.0' }
frame-system = { default-features = false, version = '2.0.0' }
pallet-assets = { path = '../assets', default-features = false, version = '2.0.0' }

[dev-dependencies]
sp-core = { default-features = false, version = '2.0.0' }
sp-io = { default-features = false, version = '2.0.0' }
sp-runtime = { default-features = false, version = '2.0.0' }

[features]
default = ['std']
std = [
'codec/std',
'frame-support/std',
'frame-system/std',
]
188 changes: 188 additions & 0 deletions pallets/assets-info/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
#![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://substrate.dev/docs/en/knowledgebase/runtime/frame
use frame_support::{
codec::{Decode, Encode},
decl_error, decl_event, decl_module, decl_storage, dispatch, ensure,
sp_runtime::RuntimeDebug,
traits::{Get, Vec},
};
use frame_system::ensure_root;

use pallet_assets as assets;

#[cfg(test)]
mod mock;

#[cfg(test)]
mod tests;

/// Configure the pallet by specifying the parameters and types on which it depends.
pub trait Trait: assets::Trait {
/// Because this pallet emits events, it depends on the runtime's definition of an event.
type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;

/// The minimum length a name may be.
type MinLengthName: Get<usize>;

/// The maximum length a name may be.
type MaxLengthName: Get<usize>;

/// The minimum length a symbol may be.
type MinLengthSymbol: Get<usize>;

/// The maximum length a symbol may be.
type MaxLengthSymbol: Get<usize>;

/// The minimum length a description may be.
type MinLengthDescription: Get<usize>;

/// The maximum length a description may be.
type MaxLengthDescription: Get<usize>;

/// The maximum decimal points an asset may be.
type MaxDecimals: Get<u32>;
}

#[derive(Encode, Decode, Clone, Default, RuntimeDebug, PartialEq, Eq)]
pub struct AssetInfo {
name: Option<Vec<u8>>,
symbol: Option<Vec<u8>>,
description: Option<Vec<u8>>,
decimals: Option<u32>,
}

decl_storage! {
trait Store for Module<T: Trait> as AssetsInfoModule {
/// TWOX-NOTE: `AssetId` is trusted, so this is safe.
AssetsInfo get(fn get_info): map hasher(twox_64_concat) T::AssetId => AssetInfo;
}
}

// Pallets use events to inform users when important changes are made.
// https://substrate.dev/docs/en/knowledgebase/runtime/events
decl_event!(
pub enum Event<T>
where
AssetId = <T as assets::Trait>::AssetId,
{
/// Asset info stored. [assetId, info]
InfoStored(AssetId, AssetInfo),
}
);

// Errors inform users that something went wrong.
decl_error! {
pub enum Error for Module<T: Trait> {
/// A name is too short.
TooShortName,
/// A name is too long.
TooLongName,
/// A symbol is too short.
TooShortSymbol,
/// A symbol is too long.
TooLongSymbol,
/// A description is too short.
TooShortDescription,
/// A description is too long.
TooLongDescription,
/// A decimals point value is out of range
DecimalsOutOfRange,
/// Asset does not exist
AssetNotExist,
}
}

decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {

type Error = Error<T>;

fn deposit_event() = default;

#[weight = 10_000 + T::DbWeight::get().writes(1) + T::DbWeight::get().reads(1)]
pub fn set_info(origin, asset: T::AssetId, name: Option<Vec<u8>>, symbol: Option<Vec<u8>>, description: Option<Vec<u8>>, decimals: Option<u32>) -> dispatch::DispatchResult {
ensure_root(origin)?;

let info = Self::set_asset_info(asset, name, symbol, description, decimals)?;

Self::deposit_event(RawEvent::InfoStored(asset, info));

Ok(())
}
}
}

impl<T: Trait> Module<T> {
pub fn set_asset_info(
asset: T::AssetId,
name: Option<Vec<u8>>,
symbol: Option<Vec<u8>>,
description: Option<Vec<u8>>,
decimals: Option<u32>,
) -> Result<AssetInfo, Error<T>> {
// is this the correct approach, could be a separate fn at least ?
#[cfg(not(test))]
{
let id = <assets::Module<T>>::next_asset_id();
ensure!(asset < id, Error::<T>::AssetNotExist);
}

let current: AssetInfo = Self::get_info(asset);

let info = AssetInfo {
name: name.or(current.name),
symbol: symbol.or(current.symbol),
description: description.or(current.description),
decimals: decimals.or(current.decimals),
};
let to_check = info.clone();

if to_check.name.is_some() {
let name = to_check.name.unwrap();
ensure!(
name.len() >= T::MinLengthName::get(),
Error::<T>::TooShortName
);
ensure!(
name.len() <= T::MaxLengthName::get(),
Error::<T>::TooLongName
);
}
if to_check.symbol.is_some() {
let sym = to_check.symbol.unwrap();
ensure!(
sym.len() >= T::MinLengthSymbol::get(),
Error::<T>::TooShortSymbol
);
ensure!(
sym.len() <= T::MaxLengthSymbol::get(),
Error::<T>::TooLongSymbol
);
}
if to_check.description.is_some() {
let desc = to_check.description.unwrap();
ensure!(
desc.len() >= T::MinLengthDescription::get(),
Error::<T>::TooShortDescription
);
ensure!(
desc.len() <= T::MaxLengthDescription::get(),
Error::<T>::TooLongDescription
);
}
if to_check.decimals.is_some() {
let decimals = to_check.decimals.unwrap();
ensure!(
decimals <= T::MaxDecimals::get() as u32,
Error::<T>::DecimalsOutOfRange
);
}

<AssetsInfo<T>>::insert(asset, info.clone());

Ok(info)
}
}
82 changes: 82 additions & 0 deletions pallets/assets-info/src/mock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use crate::{Module, Trait};
use sp_core::H256;
use frame_support::{impl_outer_origin, parameter_types, weights::Weight};
use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup}, testing::Header, Perbill,
};
use frame_system as system;
use pallet_assets as assets;

impl_outer_origin! {
pub enum Origin for Test {}
}

// Configure a mock runtime to test the pallet.

#[derive(Clone, Eq, PartialEq)]
pub struct Test;
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: Weight = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);
pub const MinLengthName: usize = 0;
pub const MaxLengthName: usize = 32;
pub const MinLengthSymbol: usize = 3;
pub const MaxLengthSymbol: usize = 8;
pub const MinLengthDescription: usize = 0;
pub const MaxLengthDescription: usize = 255;
pub const MaxDecimals: u32 = 10;
}

impl system::Trait for Test {
type BaseCallFilter = ();
type Origin = Origin;
type Call = ();
type Index = u64;
type BlockNumber = u64;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type Event = ();
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type DbWeight = ();
type BlockExecutionWeight = ();
type ExtrinsicBaseWeight = ();
type MaximumExtrinsicWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
type AvailableBlockRatio = AvailableBlockRatio;
type Version = ();
type PalletInfo = ();
type AccountData = ();
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
}

impl assets::Trait for Test {
type Event = ();
type Balance = u64;
type AssetId = u32;
}

impl Trait for Test {
type Event = ();
type MinLengthName = MinLengthName;
type MaxLengthName = MaxLengthName;
type MinLengthSymbol = MinLengthSymbol;
type MaxLengthSymbol = MaxLengthSymbol;
type MinLengthDescription = MinLengthDescription;
type MaxLengthDescription = MaxLengthDescription;
type MaxDecimals = MaxDecimals;
}

pub type AssetsInfoModule = Module<Test>;

// Build genesis storage according to the mock runtime.
pub fn new_test_ext() -> sp_io::TestExternalities {
system::GenesisConfig::default().build_storage::<Test>().unwrap().into()
}
69 changes: 69 additions & 0 deletions pallets/assets-info/src/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use crate::{mock::*, AssetInfo, Error};
use frame_support::{assert_noop, assert_ok};

#[test]
fn set_info_and_retrieve_works_ok() {
new_test_ext().execute_with(|| {
const ASSET_ID: u32 = 0;
let info = AssetInfo {
name: Some(b"name".to_vec()),
symbol: Some(b"SYM".to_vec()),
description: Some(b"desc".to_vec()),
decimals: Some(8),
};
// Dispatch a signed extrinsic.
assert_ok!(AssetsInfoModule::set_info(
Origin::root(),
ASSET_ID,
info.name.clone(),
info.symbol.clone(),
info.description.clone(),
info.decimals.clone(),
));
// Read pallet storage and assert an expected result.
assert_eq!(AssetsInfoModule::get_info(ASSET_ID), info);
});
}

#[test]
fn set_info_optional_and_retrieve_works_ok() {
new_test_ext().execute_with(|| {
const ASSET_ID: u32 = 0;
let info = AssetInfo {
name: None,
symbol: Some(b"SYM".to_vec()),
description: None,
decimals: Some(8),
};
// Dispatch a signed extrinsic.
assert_ok!(AssetsInfoModule::set_info(
Origin::root(),
ASSET_ID,
None,
// None,
info.symbol.clone(),
None,
info.decimals,
));
// Read pallet storage and assert an expected result.
assert_eq!(AssetsInfoModule::get_info(ASSET_ID), info);
});
}

#[test]
fn correct_error_for_invalid_symbol_value() {
new_test_ext().execute_with(|| {
// Ensure the expected error is thrown when no value is present.
assert_noop!(
AssetsInfoModule::set_info(
Origin::root(),
0,
None,
Some(vec![]),
None,
None,
),
Error::<Test>::TooShortSymbol
);
});
}
Loading

0 comments on commit 3175b0d

Please sign in to comment.