Skip to content

Commit

Permalink
Add Incentives Precompile (#2192)
Browse files Browse the repository at this point in the history
* add incentives precompile

* add benchmark pricing to precompile

* add unit tests

* fix runtimes and improve tests

* fix benchmarking

* add additional actions

* fix precompile

* fmt whitespace

* more whitespacem fix

* aaaa

* add some docs

* uint128 to uint256

* update to encode dynamic arrays correctly
  • Loading branch information
ferrell-code authored Jun 16, 2022
1 parent 67c72c9 commit 132dbbe
Show file tree
Hide file tree
Showing 13 changed files with 908 additions and 77 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

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

126 changes: 77 additions & 49 deletions modules/incentives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,12 @@ use frame_support::{log, pallet_prelude::*, transactional, PalletId};
use frame_system::pallet_prelude::*;
use orml_traits::{Happened, MultiCurrency, RewardHandler};
use primitives::{Amount, Balance, CurrencyId};
use scale_info::TypeInfo;
use sp_runtime::{
traits::{AccountIdConversion, One, UniqueSaturatedInto, Zero},
DispatchResult, FixedPointNumber, Permill, RuntimeDebug,
DispatchResult, FixedPointNumber, Permill,
};
use sp_std::{collections::btree_map::BTreeMap, prelude::*};
use support::{CDPTreasury, DEXIncentives, DEXManager, EmergencyShutdown, Rate};
use support::{CDPTreasury, DEXIncentives, DEXManager, EmergencyShutdown, IncentivesManager, PoolId, Rate};

mod mock;
mod tests;
Expand All @@ -61,16 +60,6 @@ pub mod weights;
pub use module::*;
pub use weights::WeightInfo;

/// PoolId for various rewards pools
#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub enum PoolId {
/// Rewards and shares pool for users who open CDP(CollateralCurrencyId)
Loans(CurrencyId),

/// Rewards and shares pool for DEX makers who stake LP token(LPCurrencyId)
Dex(CurrencyId),
}

#[frame_support::pallet]
pub mod module {
use super::*;
Expand Down Expand Up @@ -296,42 +285,7 @@ pub mod module {
#[transactional]
pub fn claim_rewards(origin: OriginFor<T>, pool_id: PoolId) -> DispatchResult {
let who = ensure_signed(origin)?;

// orml_rewards will claim rewards for all currencies rewards
<orml_rewards::Pallet<T>>::claim_rewards(&who, &pool_id);

let pending_multi_rewards: BTreeMap<CurrencyId, Balance> = PendingMultiRewards::<T>::take(&pool_id, &who);
let deduction_rate = Self::claim_reward_deduction_rates(&pool_id);

for (currency_id, pending_reward) in pending_multi_rewards {
if pending_reward.is_zero() {
continue;
}
// calculate actual rewards and deduction amount
let (actual_amount, deduction_amount) = {
let deduction_amount = deduction_rate.saturating_mul_int(pending_reward).min(pending_reward);
if !deduction_amount.is_zero() {
// re-accumulate deduction to rewards pool if deduction amount is not zero
<orml_rewards::Pallet<T>>::accumulate_reward(&pool_id, currency_id, deduction_amount)?;
}
(pending_reward.saturating_sub(deduction_amount), deduction_amount)
};

// transfer the actual reward(pending reward exclude deduction) to user from the pool. it should not
// affect the process, ignore the result to continue. if it fails, just the user will not
// be rewarded, there will not increase user balance.
T::Currency::transfer(currency_id, &Self::account_id(), &who, actual_amount)?;

Self::deposit_event(Event::ClaimRewards {
who: who.clone(),
pool: pool_id,
reward_currency_id: currency_id,
actual_amount,
deduction_amount,
});
}

Ok(())
Self::do_claim_rewards(who, pool_id)
}

/// Update incentive reward amount for specific PoolId
Expand Down Expand Up @@ -545,6 +499,44 @@ impl<T: Config> Pallet<T> {
}
}
}

fn do_claim_rewards(who: T::AccountId, pool_id: PoolId) -> DispatchResult {
// orml_rewards will claim rewards for all currencies rewards
<orml_rewards::Pallet<T>>::claim_rewards(&who, &pool_id);

let pending_multi_rewards: BTreeMap<CurrencyId, Balance> = PendingMultiRewards::<T>::take(&pool_id, &who);
let deduction_rate = Self::claim_reward_deduction_rates(&pool_id);

for (currency_id, pending_reward) in pending_multi_rewards {
if pending_reward.is_zero() {
continue;
}
// calculate actual rewards and deduction amount
let (actual_amount, deduction_amount) = {
let deduction_amount = deduction_rate.saturating_mul_int(pending_reward).min(pending_reward);
if !deduction_amount.is_zero() {
// re-accumulate deduction to rewards pool if deduction amount is not zero
<orml_rewards::Pallet<T>>::accumulate_reward(&pool_id, currency_id, deduction_amount)?;
}
(pending_reward.saturating_sub(deduction_amount), deduction_amount)
};

// transfer the actual reward(pending reward exclude deduction) to user from the pool. it should not
// affect the process, ignore the result to continue. if it fails, just the user will not
// be rewarded, there will not increase user balance.
T::Currency::transfer(currency_id, &Self::account_id(), &who, actual_amount)?;

Self::deposit_event(Event::ClaimRewards {
who: who.clone(),
pool: pool_id,
reward_currency_id: currency_id,
actual_amount,
deduction_amount,
});
}

Ok(())
}
}

impl<T: Config> DEXIncentives<T::AccountId, CurrencyId, Balance> for Pallet<T> {
Expand Down Expand Up @@ -581,6 +573,42 @@ impl<T: Config> DEXIncentives<T::AccountId, CurrencyId, Balance> for Pallet<T> {
}
}

impl<T: Config> IncentivesManager<T::AccountId, Balance, CurrencyId, PoolId> for Pallet<T> {
fn get_incentive_reward_amount(pool_id: PoolId, currency_id: CurrencyId) -> Balance {
IncentiveRewardAmounts::<T>::get(pool_id, currency_id)
}

fn get_dex_reward_rate(pool_id: PoolId) -> Rate {
DexSavingRewardRates::<T>::get(pool_id)
}

fn deposit_dex_share(who: &T::AccountId, lp_currency_id: CurrencyId, amount: Balance) -> DispatchResult {
Self::do_deposit_dex_share(who, lp_currency_id, amount)
}

fn withdraw_dex_share(who: &T::AccountId, lp_currency_id: CurrencyId, amount: Balance) -> DispatchResult {
Self::do_withdraw_dex_share(who, lp_currency_id, amount)
}

fn claim_rewards(who: T::AccountId, pool_id: PoolId) -> DispatchResult {
Self::do_claim_rewards(who, pool_id)
}

fn get_claim_reward_deduction_rate(pool_id: PoolId) -> Rate {
ClaimRewardDeductionRates::<T>::get(pool_id)
}

fn get_pending_rewards(pool_id: PoolId, who: T::AccountId, reward_currencies: Vec<CurrencyId>) -> Vec<Balance> {
let rewards_map = PendingMultiRewards::<T>::get(pool_id, who);
let mut reward_balances = Vec::new();
for reward_currency in reward_currencies {
let reward_amount = rewards_map.get(&reward_currency).copied().unwrap_or_default();
reward_balances.push(reward_amount);
}
reward_balances
}
}

pub struct OnUpdateLoan<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> Happened<(T::AccountId, CurrencyId, Amount, Balance)> for OnUpdateLoan<T> {
fn happened(info: &(T::AccountId, CurrencyId, Amount, Balance)) {
Expand Down
67 changes: 67 additions & 0 deletions modules/support/src/incentives.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// This file is part of Acala.

// Copyright (C) 2020-2022 Acala Foundation.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use crate::Rate;
use codec::{Decode, Encode};
use primitives::CurrencyId;
use scale_info::TypeInfo;
use sp_runtime::{DispatchResult, RuntimeDebug};
use sp_std::prelude::*;

/// PoolId for various rewards pools
#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub enum PoolId {
/// Rewards and shares pool for users who open CDP(CollateralCurrencyId)
Loans(CurrencyId),

/// Rewards and shares pool for DEX makers who stake LP token(LPCurrencyId)
Dex(CurrencyId),
}

pub trait IncentivesManager<AccountId, Balance, CurrencyId, PoolId> {
/// Gets reward amount for the given reward currency added per period
fn get_incentive_reward_amount(pool_id: PoolId, currency_id: CurrencyId) -> Balance;
/// Gets fixed reward rate for `PoolId::Dex` per period
fn get_dex_reward_rate(pool_id: PoolId) -> Rate;
/// Stake LP token to add shares to pool
fn deposit_dex_share(who: &AccountId, lp_currency_id: CurrencyId, amount: Balance) -> DispatchResult;
/// Unstake LP token to remove shares from pool
fn withdraw_dex_share(who: &AccountId, lp_currency_id: CurrencyId, amount: Balance) -> DispatchResult;
/// Claim all available rewards for specific `PoolId`
fn claim_rewards(who: AccountId, pool_id: PoolId) -> DispatchResult;
/// Gets deduction reate for claiming reward early
fn get_claim_reward_deduction_rate(pool_id: PoolId) -> Rate;
/// Gets the pending rewards for a pool, for an account
fn get_pending_rewards(pool_id: PoolId, who: AccountId, reward_currency: Vec<CurrencyId>) -> Vec<Balance>;
}

pub trait DEXIncentives<AccountId, CurrencyId, Balance> {
fn do_deposit_dex_share(who: &AccountId, lp_currency_id: CurrencyId, amount: Balance) -> DispatchResult;
fn do_withdraw_dex_share(who: &AccountId, lp_currency_id: CurrencyId, amount: Balance) -> DispatchResult;
}

#[cfg(feature = "std")]
impl<AccountId, CurrencyId, Balance> DEXIncentives<AccountId, CurrencyId, Balance> for () {
fn do_deposit_dex_share(_: &AccountId, _: CurrencyId, _: Balance) -> DispatchResult {
Ok(())
}

fn do_withdraw_dex_share(_: &AccountId, _: CurrencyId, _: Balance) -> DispatchResult {
Ok(())
}
}
18 changes: 2 additions & 16 deletions modules/support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ pub mod dex;
pub mod evm;
pub mod homa;
pub mod honzon;
pub mod incentives;
pub mod mocks;

pub use crate::dex::*;
pub use crate::evm::*;
pub use crate::homa::*;
pub use crate::honzon::*;
pub use crate::incentives::*;

pub type Price = FixedU128;
pub type ExchangeRate = FixedU128;
Expand Down Expand Up @@ -69,22 +71,6 @@ pub trait ExchangeRateProvider {
fn get_exchange_rate() -> ExchangeRate;
}

pub trait DEXIncentives<AccountId, CurrencyId, Balance> {
fn do_deposit_dex_share(who: &AccountId, lp_currency_id: CurrencyId, amount: Balance) -> DispatchResult;
fn do_withdraw_dex_share(who: &AccountId, lp_currency_id: CurrencyId, amount: Balance) -> DispatchResult;
}

#[cfg(feature = "std")]
impl<AccountId, CurrencyId, Balance> DEXIncentives<AccountId, CurrencyId, Balance> for () {
fn do_deposit_dex_share(_: &AccountId, _: CurrencyId, _: Balance) -> DispatchResult {
Ok(())
}

fn do_withdraw_dex_share(_: &AccountId, _: CurrencyId, _: Balance) -> DispatchResult {
Ok(())
}
}

pub trait TransactionPayment<AccountId, Balance, NegativeImbalance> {
fn reserve_fee(who: &AccountId, fee: Balance, named: Option<ReserveIdentifier>) -> Result<Balance, DispatchError>;
fn unreserve_fee(who: &AccountId, fee: Balance, named: Option<ReserveIdentifier>) -> Balance;
Expand Down
4 changes: 2 additions & 2 deletions runtime/acala/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ use module_currencies::BasicCurrencyAdapter;
use module_evm::{runner::RunnerExtended, CallInfo, CreateInfo, EvmChainId, EvmTask};
use module_evm_accounts::EvmAddressMapping;
use module_relaychain::RelayChainCallBuilder;
use module_support::{AssetIdMapping, DispatchableTask};
use module_support::{AssetIdMapping, DispatchableTask, PoolId};
use module_transaction_payment::TargetedFeeAdjustment;

use cumulus_pallet_parachain_system::RelaychainBlockNumberProvider;
Expand Down Expand Up @@ -1218,7 +1218,7 @@ impl module_asset_registry::Config for Runtime {
impl orml_rewards::Config for Runtime {
type Share = Balance;
type Balance = Balance;
type PoolId = module_incentives::PoolId;
type PoolId = PoolId;
type CurrencyId = CurrencyId;
type Handler = Incentives;
}
Expand Down
4 changes: 4 additions & 0 deletions runtime/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ module-honzon = { path = "../../modules/honzon", default-features = false }
module-loans = { path = "../../modules/loans", default-features = false }
module-cdp-engine = { path = "../../modules/cdp-engine", default-features = false }
module-cdp-treasury = { path = "../../modules/cdp-treasury", default-features = false, optional = true }
module-incentives = { path = "../../modules/incentives", default-features = false }

# orml
orml-oracle = { path = "../../orml/oracle", default-features = false }
Expand All @@ -72,6 +73,7 @@ orml-bencher = { path = "../../orml/bencher", default-features = false, optional
orml-tokens = { path = "../../orml/tokens", default-features = false, optional = true }
orml-nft = { path = "../../orml/nft", default-features = false, optional = true }
orml-currencies = { path = "../../orml/currencies", default-features = false, optional = true }
orml-rewards = { path = "../../orml/rewards", default-features = false, optional = true }

[features]
default = ["std"]
Expand Down Expand Up @@ -106,6 +108,7 @@ std = [
"orml-tokens/std",
"orml-nft/std",
"orml-currencies/std",
"orml-rewards/std",

"module-evm/std",
"module-idle-scheduler/std",
Expand All @@ -124,6 +127,7 @@ std = [
"module-cdp-engine/std",
"module-honzon/std",
"module-cdp-treasury/std",
"module-incentives/std",

"xcm/std",
"xcm-executor/std",
Expand Down
2 changes: 1 addition & 1 deletion runtime/common/src/precompile/honzon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ mod tests {
000000000000000000000000 0000000000000000000100000000000000000002
"};

// value for FixedU128 of (1/10), default value for exchange rate
// value for FixedU128 of 1, default value for exchange rate
let expected_output = hex! {"
00000000000000000000000000000000 00000000000000000de0b6b3a7640000
"};
Expand Down
Loading

0 comments on commit 132dbbe

Please sign in to comment.