Skip to content

Commit

Permalink
Implement pallet-asset-rewards (paritytech#3926)
Browse files Browse the repository at this point in the history
Closes paritytech#3149 

## Description

This PR introduces `pallet-asset-rewards`, which allows accounts to be
rewarded for freezing `fungible` tokens. The motivation for creating
this pallet is to allow incentivising LPs.

See the pallet docs for more info about the pallet.

## Runtime changes

The pallet has been added to
- `asset-hub-rococo`
- `asset-hub-westend`

The `NativeAndAssets` `fungibles` Union did not contain `PoolAssets`, so
it has been renamed `NativeAndNonPoolAssets`

A new `fungibles` Union `NativeAndAllAssets` was created to encompass
all assets and the native token.

## TODO
- [x] Emulation tests
- [x] Fill in Freeze logic (blocked
paritytech#3342) and re-run
benchmarks

---------

Co-authored-by: command-bot <>
Co-authored-by: Oliver Tale-Yazdi <[email protected]>
Co-authored-by: muharem <[email protected]>
Co-authored-by: Guillaume Thiolliere <[email protected]>
  • Loading branch information
4 people authored and Nathy-bajo committed Jan 21, 2025
1 parent 2db0193 commit d854fd7
Show file tree
Hide file tree
Showing 37 changed files with 4,517 additions and 53 deletions.
27 changes: 27 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ members = [
"substrate/frame/asset-conversion",
"substrate/frame/asset-conversion/ops",
"substrate/frame/asset-rate",
"substrate/frame/asset-rewards",
"substrate/frame/assets",
"substrate/frame/assets-freezer",
"substrate/frame/atomic-swap",
Expand Down Expand Up @@ -893,6 +894,7 @@ pallet-asset-conversion = { path = "substrate/frame/asset-conversion", default-f
pallet-asset-conversion-ops = { path = "substrate/frame/asset-conversion/ops", default-features = false }
pallet-asset-conversion-tx-payment = { path = "substrate/frame/transaction-payment/asset-conversion-tx-payment", default-features = false }
pallet-asset-rate = { path = "substrate/frame/asset-rate", default-features = false }
pallet-asset-rewards = { path = "substrate/frame/asset-rewards", default-features = false }
pallet-asset-tx-payment = { path = "substrate/frame/transaction-payment/asset-tx-payment", default-features = false }
pallet-assets = { path = "substrate/frame/assets", default-features = false }
pallet-assets-freezer = { path = "substrate/frame/assets-freezer", default-features = false }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ workspace = true

# Substrate
frame-support = { workspace = true }
pallet-asset-rewards = { workspace = true }
sp-core = { workspace = true }
sp-keyring = { workspace = true }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ pub const USDT_ID: u32 = 1984;

pub const PENPAL_A_ID: u32 = 2000;
pub const PENPAL_B_ID: u32 = 2001;
pub const ASSET_HUB_ROCOCO_ID: u32 = 1000;
pub const ASSET_HUB_WESTEND_ID: u32 = 1000;
pub const ASSETS_PALLET_ID: u8 = 50;

parameter_types! {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ codec = { workspace = true }
# Substrate
frame-support = { workspace = true }
pallet-asset-conversion = { workspace = true }
pallet-asset-rewards = { workspace = true }
pallet-assets = { workspace = true }
pallet-balances = { workspace = true }
pallet-message-queue = { workspace = true }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,11 @@ mod imports {
genesis::ED as ROCOCO_ED,
rococo_runtime::{
governance as rococo_governance,
governance::pallet_custom_origins::Origin::Treasurer,
xcm_config::{
UniversalLocation as RococoUniversalLocation, XcmConfig as RococoXcmConfig,
},
OriginCaller as RococoOriginCaller,
Dmp, OriginCaller as RococoOriginCaller,
},
RococoRelayPallet as RococoPallet,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
mod claim_assets;
mod hybrid_transfers;
mod reserve_transfer;
mod reward_pool;
mod send;
mod set_xcm_versions;
mod swap;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// 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.

use crate::imports::*;
use codec::Encode;
use frame_support::{assert_ok, sp_runtime::traits::Dispatchable, traits::schedule::DispatchTime};
use xcm_executor::traits::ConvertLocation;

#[test]
fn treasury_creates_asset_reward_pool() {
AssetHubRococo::execute_with(|| {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
type Balances = <AssetHubRococo as AssetHubRococoPallet>::Balances;

let treasurer =
Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]);
let treasurer_account =
ahr_xcm_config::LocationToAccountId::convert_location(&treasurer).unwrap();

assert_ok!(Balances::force_set_balance(
<AssetHubRococo as Chain>::RuntimeOrigin::root(),
treasurer_account.clone().into(),
ASSET_HUB_ROCOCO_ED * 100_000,
));

let events = AssetHubRococo::events();
match events.iter().last() {
Some(RuntimeEvent::Balances(pallet_balances::Event::BalanceSet { who, .. })) =>
assert_eq!(*who, treasurer_account),
_ => panic!("Expected Balances::BalanceSet event"),
}
});

Rococo::execute_with(|| {
type AssetHubRococoRuntimeCall = <AssetHubRococo as Chain>::RuntimeCall;
type AssetHubRococoRuntime = <AssetHubRococo as Chain>::Runtime;
type RococoRuntimeCall = <Rococo as Chain>::RuntimeCall;
type RococoRuntime = <Rococo as Chain>::Runtime;
type RococoRuntimeEvent = <Rococo as Chain>::RuntimeEvent;
type RococoRuntimeOrigin = <Rococo as Chain>::RuntimeOrigin;

Dmp::make_parachain_reachable(AssetHubRococo::para_id());

let staked_asset_id = bx!(RelayLocation::get());
let reward_asset_id = bx!(RelayLocation::get());

let reward_rate_per_block = 1_000_000_000;
let lifetime = 1_000_000_000;
let admin = None;

let create_pool_call =
RococoRuntimeCall::XcmPallet(pallet_xcm::Call::<RococoRuntime>::send {
dest: bx!(VersionedLocation::V4(
xcm::v4::Junction::Parachain(AssetHubRococo::para_id().into()).into()
)),
message: bx!(VersionedXcm::V5(Xcm(vec![
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
Transact {
origin_kind: OriginKind::SovereignAccount,
fallback_max_weight: None,
call: AssetHubRococoRuntimeCall::AssetRewards(
pallet_asset_rewards::Call::<AssetHubRococoRuntime>::create_pool {
staked_asset_id,
reward_asset_id,
reward_rate_per_block,
expiry: DispatchTime::After(lifetime),
admin
}
)
.encode()
.into(),
}
]))),
});

let treasury_origin: RococoRuntimeOrigin = Treasurer.into();
assert_ok!(create_pool_call.dispatch(treasury_origin));

assert_expected_events!(
Rococo,
vec![
RococoRuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {},
]
);
});

AssetHubRococo::execute_with(|| {
type Runtime = <AssetHubRococo as Chain>::Runtime;
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;

assert_eq!(1, pallet_asset_rewards::Pools::<Runtime>::iter().count());

let events = AssetHubRococo::events();
match events.iter().last() {
Some(RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed {
success: true,
..
})) => (),
_ => panic!("Expected MessageQueue::Processed event"),
}
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ frame-metadata-hash-extension = { workspace = true, default-features = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
pallet-asset-conversion = { workspace = true }
pallet-asset-rewards = { workspace = true }
pallet-asset-tx-payment = { workspace = true }
pallet-assets = { workspace = true }
pallet-balances = { workspace = true }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,12 @@ mod imports {
},
westend_emulated_chain::{
genesis::ED as WESTEND_ED,
westend_runtime::xcm_config::{
UniversalLocation as WestendUniversalLocation, XcmConfig as WestendXcmConfig,
westend_runtime::{
governance::pallet_custom_origins::Origin::Treasurer,
xcm_config::{
UniversalLocation as WestendUniversalLocation, XcmConfig as WestendXcmConfig,
},
Dmp,
},
WestendRelayPallet as WestendPallet,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod claim_assets;
mod fellowship_treasury;
mod hybrid_transfers;
mod reserve_transfer;
mod reward_pool;
mod send;
mod set_asset_claimer;
mod set_xcm_versions;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// 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.

use crate::imports::*;
use codec::Encode;
use frame_support::{assert_ok, sp_runtime::traits::Dispatchable, traits::schedule::DispatchTime};
use xcm_executor::traits::ConvertLocation;

#[test]
fn treasury_creates_asset_reward_pool() {
AssetHubWestend::execute_with(|| {
type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
type Balances = <AssetHubWestend as AssetHubWestendPallet>::Balances;

let treasurer =
Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]);
let treasurer_account =
ahw_xcm_config::LocationToAccountId::convert_location(&treasurer).unwrap();

assert_ok!(Balances::force_set_balance(
<AssetHubWestend as Chain>::RuntimeOrigin::root(),
treasurer_account.clone().into(),
ASSET_HUB_WESTEND_ED * 100_000,
));

let events = AssetHubWestend::events();
match events.iter().last() {
Some(RuntimeEvent::Balances(pallet_balances::Event::BalanceSet { who, .. })) =>
assert_eq!(*who, treasurer_account),
_ => panic!("Expected Balances::BalanceSet event"),
}
});
Westend::execute_with(|| {
type AssetHubWestendRuntimeCall = <AssetHubWestend as Chain>::RuntimeCall;
type AssetHubWestendRuntime = <AssetHubWestend as Chain>::Runtime;
type WestendRuntimeCall = <Westend as Chain>::RuntimeCall;
type WestendRuntime = <Westend as Chain>::Runtime;
type WestendRuntimeEvent = <Westend as Chain>::RuntimeEvent;
type WestendRuntimeOrigin = <Westend as Chain>::RuntimeOrigin;

Dmp::make_parachain_reachable(AssetHubWestend::para_id());

let staked_asset_id = bx!(RelayLocation::get());
let reward_asset_id = bx!(RelayLocation::get());

let reward_rate_per_block = 1_000_000_000;
let lifetime = 1_000_000_000;
let admin = None;

let create_pool_call =
WestendRuntimeCall::XcmPallet(pallet_xcm::Call::<WestendRuntime>::send {
dest: bx!(VersionedLocation::V4(
xcm::v4::Junction::Parachain(AssetHubWestend::para_id().into()).into()
)),
message: bx!(VersionedXcm::V5(Xcm(vec![
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
Transact {
origin_kind: OriginKind::SovereignAccount,
fallback_max_weight: None,
call: AssetHubWestendRuntimeCall::AssetRewards(
pallet_asset_rewards::Call::<AssetHubWestendRuntime>::create_pool {
staked_asset_id,
reward_asset_id,
reward_rate_per_block,
expiry: DispatchTime::After(lifetime),
admin
}
)
.encode()
.into(),
}
]))),
});

let treasury_origin: WestendRuntimeOrigin = Treasurer.into();
assert_ok!(create_pool_call.dispatch(treasury_origin));

assert_expected_events!(
Westend,
vec![
WestendRuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {},
]
);
});

AssetHubWestend::execute_with(|| {
type Runtime = <AssetHubWestend as Chain>::Runtime;
type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;

assert_eq!(1, pallet_asset_rewards::Pools::<Runtime>::iter().count());

let events = AssetHubWestend::events();
match events.iter().last() {
Some(RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed {
success: true,
..
})) => (),
_ => panic!("Expected MessageQueue::Processed event"),
}
});
}
Loading

0 comments on commit d854fd7

Please sign in to comment.