Skip to content

Commit

Permalink
Unknown tokens pallet (open-web3-stack#411)
Browse files Browse the repository at this point in the history
* Impl unknown tokens pallet.

* Fix workspace pallet path.

* Make clippy happy.

* Clippy, be happy.

* Unit tests.
  • Loading branch information
shaunxw authored Mar 19, 2021
1 parent 83b05e2 commit 918da40
Show file tree
Hide file tree
Showing 7 changed files with 446 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.dev.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ members = [
"nft",
"xtokens",
"xcm-support",
"unknown-tokens",
]
resolver = "2"

Expand Down
36 changes: 36 additions & 0 deletions unknown-tokens/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[package]
name = "orml-unknown-tokens"
description = "Unknown tokens module that implements `UnknownAsset` trait."
repository = "https://github.com/open-web3-stack/open-runtime-module-library/tree/master/unknown-tokens"
license = "Apache-2.0"
version = "0.4.1-dev"
authors = ["Acala Developers"]
edition = "2018"

[dependencies]
serde = { version = "1.0.124", optional = true }
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false }

xcm = { git = "https://github.com/paritytech/polkadot", branch = "rococo-v1", default-features = false }

orml-xcm-support = { path = "../xcm-support", default-features = false }

[dev-dependencies]
sp-io = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1" }

[features]
default = ["std"]
std = [
"serde",
"codec/std",
"sp-std/std",
"frame-support/std",
"frame-system/std",
"xcm/std",
"orml-xcm-support/std",
]
127 changes: 127 additions & 0 deletions unknown-tokens/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#![cfg_attr(not(feature = "std"), no_std)]
#![allow(clippy::unused_unit)]

use frame_support::pallet_prelude::*;
use sp_std::vec::Vec;
use xcm::v0::{MultiAsset, MultiLocation};

use orml_xcm_support::UnknownAsset;

pub use module::*;

mod mock;
mod tests;

#[frame_support::pallet]
pub mod module {
use super::*;

#[pallet::config]
pub trait Config: frame_system::Config {
type Event: From<Event> + IsType<<Self as frame_system::Config>::Event>;
}

#[pallet::event]
#[pallet::generate_deposit(pub(crate) fn deposit_event)]
pub enum Event {
/// Deposit success. [asset, to]
Deposited(MultiAsset, MultiLocation),
/// Deposit failed. [asset, to, error]
DepositFailed(MultiAsset, MultiLocation, DispatchError),
/// Withdraw success. [asset, from]
Withdrawn(MultiAsset, MultiLocation),
/// Withdraw failed. [asset, from, error]
WithdrawFailed(MultiAsset, MultiLocation, DispatchError),
}

#[pallet::error]
pub enum Error<T> {
/// The balance is too low.
BalanceTooLow,
/// The operation will cause balance to overflow.
BalanceOverflow,
/// Unhandled asset.
UnhandledAsset,
}

#[pallet::pallet]
pub struct Pallet<T>(_);

#[pallet::hooks]
impl<T: Config> Hooks<T::BlockNumber> for Pallet<T> {}

/// Concrete fungible balances under a given location and a concrete
/// fungible id.
///
/// double_map: who, asset_id => u128
#[pallet::storage]
#[pallet::getter(fn concrete_fungible_balances)]
pub(crate) type ConcreteFungibleBalances<T> =
StorageDoubleMap<_, Blake2_128Concat, MultiLocation, Blake2_128Concat, MultiLocation, u128, ValueQuery>;

/// Abstract fungible balances under a given location and a abstract
/// fungible id.
///
/// double_map: who, asset_id => u128
#[pallet::storage]
#[pallet::getter(fn abstract_fungible_balances)]
pub(crate) type AbstractFungibleBalances<T> =
StorageDoubleMap<_, Blake2_128Concat, MultiLocation, Blake2_128Concat, Vec<u8>, u128, ValueQuery>;

#[pallet::call]
impl<T: Config> Pallet<T> {}
}

impl<T: Config> UnknownAsset for Pallet<T> {
fn deposit(asset: &MultiAsset, to: &MultiLocation) -> DispatchResult {
let result = match asset {
MultiAsset::ConcreteFungible { id, amount } => {
ConcreteFungibleBalances::<T>::try_mutate(to, id, |b| -> DispatchResult {
*b = b.checked_add(*amount).ok_or(Error::<T>::BalanceOverflow)?;
Ok(())
})
}
MultiAsset::AbstractFungible { id, amount } => {
AbstractFungibleBalances::<T>::try_mutate(to, id, |b| -> DispatchResult {
*b = b.checked_add(*amount).ok_or(Error::<T>::BalanceOverflow)?;
Ok(())
})
}
_ => Err(Error::<T>::UnhandledAsset.into()),
};

if let Err(err) = result {
Self::deposit_event(Event::DepositFailed(asset.clone(), to.clone(), err));
} else {
Self::deposit_event(Event::Deposited(asset.clone(), to.clone()));
}

result
}

fn withdraw(asset: &MultiAsset, from: &MultiLocation) -> DispatchResult {
let result = match asset {
MultiAsset::ConcreteFungible { id, amount } => {
ConcreteFungibleBalances::<T>::try_mutate(from, id, |b| -> DispatchResult {
*b = b.checked_sub(*amount).ok_or(Error::<T>::BalanceTooLow)?;
Ok(())
})
}
MultiAsset::AbstractFungible { id, amount } => {
AbstractFungibleBalances::<T>::try_mutate(from, id, |b| -> DispatchResult {
*b = b.checked_sub(*amount).ok_or(Error::<T>::BalanceTooLow)?;
Ok(())
})
}
_ => Err(Error::<T>::UnhandledAsset.into()),
};

if let Err(err) = result {
Self::deposit_event(Event::WithdrawFailed(asset.clone(), from.clone(), err));
} else {
Self::deposit_event(Event::Withdrawn(asset.clone(), from.clone()));
}

result
}
}
73 changes: 73 additions & 0 deletions unknown-tokens/src/mock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//! Mocks for the unknown pallet.
#![cfg(test)]

use super::*;
use crate as unknown_tokens;

use frame_support::{construct_runtime, parameter_types};
use sp_core::H256;
use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32};

pub type AccountId = AccountId32;

parameter_types! {
pub const BlockHashCount: u64 = 250;
}

impl frame_system::Config for Runtime {
type Origin = Origin;
type Call = Call;
type Index = u64;
type BlockNumber = u64;
type Hash = H256;
type Hashing = ::sp_runtime::traits::BlakeTwo256;
type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type Event = Event;
type BlockHashCount = BlockHashCount;
type BlockWeights = ();
type BlockLength = ();
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = ();
type OnNewAccount = ();
type OnKilledAccount = ();
type DbWeight = ();
type BaseCallFilter = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
}

impl Config for Runtime {
type Event = Event;
}

type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Runtime>;
type Block = frame_system::mocking::MockBlock<Runtime>;

construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system::{Module, Call, Storage, Config, Event<T>},
UnknownTokens: unknown_tokens::{Module, Storage, Event},
}
);

pub struct ExtBuilder;

impl ExtBuilder {
pub fn build(self) -> sp_io::TestExternalities {
let t = frame_system::GenesisConfig::default()
.build_storage::<Runtime>()
.unwrap();

let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| System::set_block_number(1));
ext
}
}
Loading

0 comments on commit 918da40

Please sign in to comment.