Skip to content

Commit

Permalink
Add eresidency required parameter to Assets
Browse files Browse the repository at this point in the history
  • Loading branch information
kacperzuk-neti committed Sep 27, 2024
1 parent da1219c commit f908817
Show file tree
Hide file tree
Showing 27 changed files with 447 additions and 381 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions liberland-extension/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ frame-system = { default-features = false, tag = "polkadot-v1.1.0", git = "https
log = { version = "0.4.17", default-features = false }

pallet-llm = { default-features = false, path = "../../substrate/frame/llm" }
pallet-assets = { default-features = false, path = "../../substrate/frame/assets" }

[features]
default = ["std"]
Expand All @@ -32,4 +33,5 @@ std = [
"frame-system/std",
"log/std",
"pallet-llm/std",
"pallet-assets/std",
]
4 changes: 3 additions & 1 deletion liberland-extension/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ use log::{error, trace};
use pallet_contracts::chain_extension::{ChainExtension, Environment, Ext, InitState, RetVal};
use sp_runtime::DispatchError;

type BalanceOfAssets<T> = <T as pallet_assets::Config>::Balance;

#[derive(Decode, Encode, MaxEncodedLen)]
pub struct LLMForceTransferArguments<T: pallet_llm::Config> {
from: pallet_llm::LLMAccount<T::AccountId>,
to: pallet_llm::LLMAccount<T::AccountId>,
amount: T::Balance,
amount: BalanceOfAssets<T>,
}

/// Contract extension for the Liberland Chain
Expand Down
2 changes: 2 additions & 0 deletions substrate/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1264,6 +1264,7 @@ impl pallet_assets::Config for Runtime {
type RemoveItemsLimit = ConstU32<1000>;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
type Citizenship = LLM;
}

ord_parameter_types! {
Expand Down Expand Up @@ -1291,6 +1292,7 @@ impl pallet_assets::Config<Instance2> for Runtime {
type CallbackHandle = ();
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
type Citizenship = LLM;
}

parameter_types! {
Expand Down
1 change: 1 addition & 0 deletions substrate/frame/assets/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ frame-support = { default-features = false, tag = "polkadot-v1.1.0", git = "http
frame-system = { default-features = false, tag = "polkadot-v1.1.0", git = "https://github.com/paritytech/polkadot-sdk" }
frame-benchmarking = { default-features = false, optional = true, tag = "polkadot-v1.1.0", git = "https://github.com/paritytech/polkadot-sdk" }
sp-core = { default-features = false, tag = "polkadot-v1.1.0", git = "https://github.com/paritytech/polkadot-sdk" }
liberland-traits = { default-features = false, path = "../liberland-traits" }

[dev-dependencies]
sp-std = { tag = "polkadot-v1.1.0", git = "https://github.com/paritytech/polkadot-sdk" }
Expand Down
16 changes: 16 additions & 0 deletions substrate/frame/assets/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -548,5 +548,21 @@ benchmarks_instance_pallet! {
assert_last_event::<T, I>(Event::Blocked { asset_id: asset_id.into(), who: caller }.into());
}

set_parameters {
let (asset_id, caller, _) = create_default_asset::<T, I>(true);
let parameters = AssetParameters { eresidency_required: true };
}: _(SystemOrigin::Signed(caller), asset_id, parameters)
verify {
assert_last_event::<T, I>(Event::ParametersSet { asset_id: asset_id.into(), parameters }.into());
}

force_set_parameters {
let (asset_id, caller, _) = create_default_asset::<T, I>(true);
let parameters = AssetParameters { eresidency_required: true };
}: _(SystemOrigin::Root, asset_id, parameters)
verify {
assert_last_event::<T, I>(Event::ParametersSet { asset_id: asset_id.into(), parameters }.into());
}

impl_benchmark_test_suite!(Assets, crate::mock::new_test_ext(), crate::mock::Test)
}
39 changes: 39 additions & 0 deletions substrate/frame/assets/src/eresidency.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
Copyright © 2024 Liberland
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

use crate::*;
use sp_runtime::DispatchResult;
use liberland_traits::CitizenshipChecker;
use frame_support::ensure;

impl<T: Config<I>, I: 'static> Pallet<T, I> {
pub fn do_set_parameters(
asset: T::AssetId,
parameters: AssetParameters,
maybe_check_owner: Option<T::AccountId>,
) -> DispatchResult {
if let Some(check_owner) = maybe_check_owner {
let d = Asset::<T, I>::get(&asset).ok_or(Error::<T, I>::Unknown)?;
ensure!(d.owner == check_owner, Error::<T, I>::NoPermission);
}

Parameters::<T, I>::insert(&asset, parameters);
Self::deposit_event(Event::ParametersSet { asset_id: asset, parameters });
Ok(())
}

pub fn maybe_ensure_eresidency(asset: T::AssetId, who: &T::AccountId) -> DispatchResult {
let AssetParameters { eresidency_required } = Parameters::<T, I>::get(asset);
if eresidency_required {
T::Citizenship::ensure_stocks_allowed(who)?
}
Ok(())
}
}
7 changes: 7 additions & 0 deletions substrate/frame/assets/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

use super::*;
use frame_support::{defensive, traits::Get, BoundedVec};
use liberland_traits::CitizenshipChecker;

#[must_use]
pub(super) enum DeadConsequence {
Expand Down Expand Up @@ -135,6 +136,12 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
if increase_supply && details.supply.checked_add(&amount).is_none() {
return DepositConsequence::Overflow
}

let AssetParameters { eresidency_required } = Parameters::<T, I>::get(&id);
if eresidency_required && T::Citizenship::ensure_stocks_allowed(who).is_err() {
return DepositConsequence::Blocked
}

if let Some(account) = Account::<T, I>::get(id, who) {
if account.status.is_blocked() {
return DepositConsequence::Blocked
Expand Down
54 changes: 54 additions & 0 deletions substrate/frame/assets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ mod functions;
mod impl_fungibles;
mod impl_stored_map;
mod types;
mod eresidency;
pub use types::*;

use scale_info::TypeInfo;
Expand Down Expand Up @@ -322,6 +323,8 @@ pub mod pallet {
/// Helper trait for benchmarks.
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper: BenchmarkHelper<Self::AssetIdParameter>;

type Citizenship: liberland_traits::CitizenshipChecker<Self::AccountId>;
}

#[pallet::storage]
Expand Down Expand Up @@ -368,6 +371,16 @@ pub mod pallet {
ValueQuery,
>;

#[pallet::storage]
/// Eresidency requirements of asset
pub(super) type Parameters<T: Config<I>, I: 'static = ()> = StorageMap<
_,
Blake2_128Concat,
T::AssetId,
AssetParameters,
ValueQuery,
>;

#[pallet::genesis_config]
#[derive(frame_support::DefaultNoBound)]
pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
Expand Down Expand Up @@ -524,6 +537,11 @@ pub mod pallet {
Touched { asset_id: T::AssetId, who: T::AccountId, depositor: T::AccountId },
/// Some account `who` was blocked.
Blocked { asset_id: T::AssetId, who: T::AccountId },
/// Asset Parameters set
ParametersSet {
asset_id: T::AssetId,
parameters: AssetParameters,
},
}

#[pallet::error]
Expand Down Expand Up @@ -1636,6 +1654,42 @@ pub mod pallet {
Self::deposit_event(Event::<T, I>::Blocked { asset_id: id, who });
Ok(())
}

/// Set the parameters for an asset.
///
/// Origin must be Signed and the sender should be the Owner of the asset `id`.
///
/// Emits `ParametersSet`.
///
/// Weight: `O(1)`
#[pallet::call_index(100)]
#[pallet::weight(T::WeightInfo::set_parameters())]
pub fn set_parameters(
origin: OriginFor<T>,
id: T::AssetIdParameter,
parameters: AssetParameters,
) -> DispatchResult {
let signer = ensure_signed(origin)?;
let id: T::AssetId = id.into();
Self::do_set_parameters(id, parameters, Some(signer))
}

/// Force set the parameters for an asset.
///
/// Origin must be ForceOrigin.
///
/// Weight: `O(1)`
#[pallet::call_index(101)]
#[pallet::weight(T::WeightInfo::force_set_parameters())]
pub fn force_set_parameters(
origin: OriginFor<T>,
id: T::AssetIdParameter,
parameters: AssetParameters,
) -> DispatchResult {
T::ForceOrigin::ensure_origin(origin)?;
let id: T::AssetId = id.into();
Self::do_set_parameters(id, parameters, None)
}
}

/// Implements [`AccountTouch`] trait.
Expand Down
7 changes: 7 additions & 0 deletions substrate/frame/assets/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup},
BuildStorage,
};
use liberland_traits::MockCitizenshipChecker;

type Block = frame_system::mocking::MockBlock<Test>;

Expand Down Expand Up @@ -131,6 +132,11 @@ impl AssetsCallbackHandle {
}
}

parameter_types! {
pub MockCitizenOne: AccountId = 100u64;
pub MockCitizenTwo: AccountId = 101u64;
}

impl Config for Test {
type RuntimeEvent = RuntimeEvent;
type Balance = u64;
Expand All @@ -152,6 +158,7 @@ impl Config for Test {
type RemoveItemsLimit = ConstU32<5>;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
type Citizenship = MockCitizenshipChecker<Self::AccountId, MockCitizenOne, MockCitizenTwo>;
}

use std::collections::HashMap;
Expand Down
60 changes: 60 additions & 0 deletions substrate/frame/assets/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use crate::{mock::*, Error};
use frame_support::{
assert_noop, assert_ok,
dispatch::GetDispatchInfo,
error::BadOrigin,
traits::{fungibles::InspectEnumerable, tokens::Preservation::Protect, Currency},
};
use pallet_balances::Error as BalancesError;
Expand Down Expand Up @@ -1775,3 +1776,62 @@ fn asset_destroy_refund_existence_deposit() {
assert_eq!(Balances::reserved_balance(&admin), 0);
});
}

#[test]
fn only_owner_can_set_parameters() {
new_test_ext().execute_with(|| {
assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1));
let admin_origin = RuntimeOrigin::signed(1);
assert_ok!(Assets::set_parameters(admin_origin, 0, AssetParameters { eresidency_required: true }));

let nonadmin_origin = RuntimeOrigin::signed(2);
assert_noop!(
Assets::set_parameters(nonadmin_origin, 0, AssetParameters { eresidency_required: true }),
Error::<Test>::NoPermission
);
});
}

#[test]
fn only_root_can_force_set_parameters() {
new_test_ext().execute_with(|| {
assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1));
let root_origin = RuntimeOrigin::root();
assert_ok!(Assets::force_set_parameters(root_origin, 0, AssetParameters { eresidency_required: true }));

let admin_origin = RuntimeOrigin::signed(1);
assert_noop!(
Assets::force_set_parameters(admin_origin, 0, AssetParameters { eresidency_required: true }),
BadOrigin
);
});
}

#[test]
fn eresident_can_receive_eresidency_only_asset() {
new_test_ext().execute_with(|| {
assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1));
let root_origin = RuntimeOrigin::root();
assert_ok!(Assets::force_set_parameters(root_origin, 0, AssetParameters { eresidency_required: true }));
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 100, 100));
assert_ok!(Assets::transfer(RuntimeOrigin::signed(100), 0, 101, 100));
});
}

#[test]
fn non_eresident_cant_receive_eresidency_only_asset() {
new_test_ext().execute_with(|| {
assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1));
assert_ok!(Assets::force_set_parameters(RuntimeOrigin::root(), 0, AssetParameters { eresidency_required: true }));
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 100, 100));

assert_noop!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100), TokenError::Blocked);
assert_noop!(Assets::transfer(RuntimeOrigin::signed(100), 0, 1, 100), TokenError::Blocked);
assert_noop!(Assets::transfer_keep_alive(RuntimeOrigin::signed(100), 0, 1, 50), TokenError::Blocked);
assert_noop!(Assets::force_transfer(RuntimeOrigin::signed(1), 0, 100, 1, 100), TokenError::Blocked);

Balances::make_free_balance_be(&100, 100);
assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(100), 0, 1, 50));
assert_noop!(Assets::transfer_approved(RuntimeOrigin::signed(1), 0, 100, 1, 50), TokenError::Blocked);
});
}
6 changes: 6 additions & 0 deletions substrate/frame/assets/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,3 +317,9 @@ where
.saturating_mul_int(balance))
}
}

#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Default, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub struct AssetParameters {
/// Is eresidency required to receive the asset
pub(super) eresidency_required: bool,
}
Loading

0 comments on commit f908817

Please sign in to comment.