Skip to content

Commit

Permalink
feat: modify asset rewards rate
Browse files Browse the repository at this point in the history
  • Loading branch information
emidev98 committed Jan 10, 2024
1 parent 2928a20 commit 9e9729c
Show file tree
Hide file tree
Showing 9 changed files with 203 additions and 12 deletions.
2 changes: 1 addition & 1 deletion contracts/alliance-hub/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ fn stake(deps: DepsMut, _env: Env, info: MessageInfo) -> Result<Response, Contra
let asset_key = AssetInfoKey::from(&asset);
WHITELIST
.load(deps.storage, asset_key.clone())
.map_err(|_| ContractError::AssetNotWhitelisted {})?;
.map_err(|_| ContractError::AssetNotWhitelisted(asset.to_string()))?;
let sender = info.sender.clone();

let rewards = _claim_reward(deps.storage, sender.clone(), asset.clone())?;
Expand Down
2 changes: 1 addition & 1 deletion contracts/alliance-hub/src/tests/stake_unstake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ fn test_stake_invalid() {
let msg = ExecuteMsg::Stake {};
let info = mock_info("user1", &[coin(100, "asset2")]);
let err = execute(deps.as_mut(), mock_env(), info, msg).unwrap_err();
assert_eq!(err, ContractError::AssetNotWhitelisted {});
assert_eq!(err, ContractError::AssetNotWhitelisted(String::from("native:asset2")));

// Stake multiple assets in a single call
let msg = ExecuteMsg::Stake {};
Expand Down
43 changes: 40 additions & 3 deletions contracts/alliance-lp-hub/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use alliance_protocol::alliance_oracle_types::EmissionsDistribution;
use alliance_protocol::alliance_protocol::AssetDistribution;

use crate::{
models::{Config, ExecuteMsg, InstantiateMsg, ModifyAsset},
models::{Config, ExecuteMsg, InstantiateMsg, ModifyAsset, ModifyAssetRewardRate},
state::{
ASSET_REWARD_RATE, BALANCES, CONFIG, TEMP_BALANCE,
TOTAL_BALANCES, UNCLAIMED_REWARDS, USER_ASSET_REWARD_RATE, VALIDATORS, WHITELIST,
Expand Down Expand Up @@ -85,6 +85,7 @@ pub fn execute(
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::ModifyAssets(assets) => modify_assets(deps, info, assets),
ExecuteMsg::ModifyAssetsRewardsRate(assets) => modify_assets_rewards_rate(deps, info, assets),

ExecuteMsg::Receive(cw20_msg) => {
let sender = deps.api.addr_validate(&cw20_msg.sender)?;
Expand Down Expand Up @@ -149,6 +150,42 @@ fn modify_assets(
}
}

Ok(Response::default().add_attributes(attrs))
}

fn modify_assets_rewards_rate(
deps: DepsMut,
info: MessageInfo,
assets: Vec<ModifyAssetRewardRate>,
) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
is_governance(&info, &config)?;

let mut attrs = vec![("action".to_string(), "modify_assets_rewards_rate".to_string())];
let mut cumulative_reward_rate = Decimal::zero();

for asset in assets {
cumulative_reward_rate += asset.reward_rate;

if cumulative_reward_rate.gt(&Decimal::one()) {
return Err(ContractError::InvalidTotalDistribution(cumulative_reward_rate));
}

let asset_key = AssetInfoKey::from(asset.asset_info.clone());
if !WHITELIST.has(deps.storage, asset_key.clone()) {
return Err(ContractError::AssetNotWhitelisted(asset.asset_info.to_string()))
}

ASSET_REWARD_RATE.update(deps.storage, asset_key, |rate| -> StdResult<_> {
Ok(rate.unwrap_or(Decimal::zero()) + asset.reward_rate)
})?;

attrs.extend_from_slice(&[
("asset".to_string(), asset.asset_info.to_string()),
("reward_rate".to_string(), asset.reward_rate.to_string()),
]);
}

Ok(Response::new().add_attributes(attrs))
}

Expand All @@ -163,7 +200,7 @@ fn stake(
let asset_key = AssetInfoKey::from(&received_asset.info);
WHITELIST
.load(deps.storage, asset_key.clone())
.map_err(|_| ContractError::AssetNotWhitelisted {})?;
.map_err(|_| ContractError::AssetNotWhitelisted(received_asset.info.to_string()))?;

let rewards = _claim_reward(deps.storage, sender.clone(), received_asset.info.clone())?;
if !rewards.is_zero() {
Expand Down Expand Up @@ -596,7 +633,7 @@ fn rebalance_emissions_callback(
if let Some(current) = current {
return Ok(current + distribution.distribution.to_decimal()?);
} else {
return Err(ContractError::AssetNotWhitelisted {});
return Err(ContractError::AssetNotWhitelisted(asset_info.to_string()));
}
})?;
}
Expand Down
25 changes: 24 additions & 1 deletion contracts/alliance-lp-hub/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,15 @@ pub enum ExecuteMsg {
Receive(Cw20ReceiveMsg),

// Used to do the other operations
// for staked assets
// related to staked assets
Unstake(Asset),
ClaimRewards(AssetInfo),
UpdateRewards {},
UpdateRewardsCallback {},

// Privileged functions
ModifyAssets(Vec<ModifyAsset>),
ModifyAssetsRewardsRate(Vec<ModifyAssetRewardRate>),

AllianceDelegate(AllianceDelegateMsg),
AllianceUndelegate(AllianceUndelegateMsg),
Expand All @@ -69,6 +70,28 @@ impl ModifyAsset {
}
}

#[cw_serde]
pub struct ModifyAssetRewardRate {
pub asset_info: AssetInfo,
pub reward_rate: Decimal,
}

impl ModifyAssetRewardRate {
pub fn new(asset_info: AssetInfo, reward_rate: Decimal) -> Self {
ModifyAssetRewardRate {
asset_info,
reward_rate,
}
}

pub fn is_valid_reward_rate(&self) -> Result<Decimal, ContractError> {
if self.reward_rate < Decimal::zero() || self.reward_rate > Decimal::one() {
return Err(ContractError::InvalidRewardRate(self.reward_rate, self.asset_info.to_string()));
}
Ok(self.reward_rate)
}
}

#[cw_serde]
#[derive(QueryResponses)]
pub enum QueryMsg {
Expand Down
12 changes: 11 additions & 1 deletion contracts/alliance-lp-hub/src/tests/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use crate::contract::{execute, instantiate};
use crate::models::{
AllPendingRewardsQuery, AssetQuery, Config, ExecuteMsg, InstantiateMsg, PendingRewardsRes,
QueryMsg, StakedBalanceRes, ModifyAsset,
QueryMsg, StakedBalanceRes, ModifyAsset, ModifyAssetRewardRate,
};
use crate::query::query;
use crate::state::CONFIG;
use alliance_protocol::alliance_protocol::{
AllianceDelegateMsg, AllianceDelegation, AllianceRedelegateMsg, AllianceRedelegation,
AllianceUndelegateMsg,
};
use alliance_protocol::error::ContractError;
use alliance_protocol::token_factory::CustomExecuteMsg;
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{coin, from_json, Deps, DepsMut, Response, StdResult, Uint128, Binary, Addr};
Expand Down Expand Up @@ -50,6 +51,15 @@ pub fn modify_asset(deps: DepsMut, assets: Vec<ModifyAsset>) -> Response {
}


pub fn modify_assets_rewards_rate(deps: DepsMut, assets: Vec<ModifyAssetRewardRate>) -> Result<Response, ContractError> {
let info = mock_info("gov", &[]);
let env = mock_env();

let msg = ExecuteMsg::ModifyAssetsRewardsRate(assets);
execute(deps, env, info, msg)
}


pub fn stake(deps: DepsMut, user: &str, amount: u128, denom: &str) -> Response {
let info = mock_info(user, &[coin(amount, denom)]);
let env = mock_env();
Expand Down
1 change: 1 addition & 0 deletions contracts/alliance-lp-hub/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ mod helpers;
mod instantiate;
mod stake_unstake;
mod rewards;
mod modify_assets;
120 changes: 120 additions & 0 deletions contracts/alliance-lp-hub/src/tests/modify_assets.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
use std::str::FromStr;

use alliance_protocol::error::ContractError;
use cosmwasm_std::{testing::mock_dependencies, Addr, Decimal, Response};
use cw_asset::AssetInfo;

use crate::{tests::helpers::{setup_contract, modify_asset, modify_assets_rewards_rate}, models::{ModifyAsset, ModifyAssetRewardRate}};

#[test]
fn test_add_assets() {
let mut deps = mock_dependencies();
setup_contract(deps.as_mut());
let res = modify_asset(
deps.as_mut(),
vec![
ModifyAsset {
asset_info: AssetInfo::native(Addr::unchecked("native_asset")),
delete: false,
},
ModifyAsset {
asset_info: AssetInfo::Cw20(Addr::unchecked("cw20_asset")),
delete: false,
}
]
);

assert_eq!(
res,
Response::default()
.add_attributes(vec![
("action", "modify_assets"),
("asset", "native:native_asset"),
("asset", "cw20:cw20_asset"),
])
);
}


#[test]
fn test_modify_rewards_rate() {
let mut deps = mock_dependencies();
setup_contract(deps.as_mut());
modify_asset(
deps.as_mut(),
vec![
ModifyAsset {
asset_info: AssetInfo::native(Addr::unchecked("native_asset")),
delete: false,
},
ModifyAsset {
asset_info: AssetInfo::Cw20(Addr::unchecked("cw20_asset")),
delete: false,
}
]
);

let res = modify_assets_rewards_rate(
deps.as_mut(),
vec![
ModifyAssetRewardRate {
asset_info: AssetInfo::native(Addr::unchecked("native_asset")),
reward_rate: Decimal::from_str("0.75").unwrap(),
},
ModifyAssetRewardRate {
asset_info: AssetInfo::Cw20(Addr::unchecked("cw20_asset")),
reward_rate: Decimal::from_str("0.25").unwrap(),
}
]
);
assert_eq!(
res.unwrap(),
Response::default()
.add_attributes(vec![
("action", "modify_assets_rewards_rate"),
("asset", "native:native_asset"),
("reward_rate", "0.75"),
("asset", "cw20:cw20_asset"),
("reward_rate", "0.25"),
])
);
}


#[test]
fn test_modify_rewards_rate_wrongly() {
let mut deps = mock_dependencies();
setup_contract(deps.as_mut());
modify_asset(
deps.as_mut(),
vec![
ModifyAsset {
asset_info: AssetInfo::native(Addr::unchecked("native_asset")),
delete: false,
},
ModifyAsset {
asset_info: AssetInfo::Cw20(Addr::unchecked("cw20_asset")),
delete: false,
}
]
);

let res = modify_assets_rewards_rate(
deps.as_mut(),
vec![
ModifyAssetRewardRate {
asset_info: AssetInfo::native(Addr::unchecked("native_asset")),
reward_rate: Decimal::from_str("0.85").unwrap(),
},
ModifyAssetRewardRate {
asset_info: AssetInfo::Cw20(Addr::unchecked("cw20_asset")),
reward_rate: Decimal::from_str("0.25").unwrap(),
}
]
);

assert_eq!(
res,
Err(ContractError::InvalidTotalDistribution(Decimal::from_str("1.1").unwrap()))
);
}
4 changes: 2 additions & 2 deletions contracts/alliance-lp-hub/src/tests/stake_unstake.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

use crate::contract::execute;
use crate::models::{ExecuteMsg, StakedBalanceRes, ModifyAsset};
use crate::state::{BALANCES, TOTAL_BALANCES};
Expand All @@ -6,9 +7,8 @@ use crate::tests::helpers::{
};
use alliance_protocol::error::ContractError;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{coin, Addr, BankMsg, CosmosMsg, Response, Uint128, Decimal};
use cosmwasm_std::{coin, Addr, BankMsg, CosmosMsg, Response, Uint128};
use cw_asset::{Asset, AssetInfo, AssetInfoKey};
use std::collections::HashMap;

#[test]
fn test_stake() {
Expand Down
6 changes: 3 additions & 3 deletions packages/alliance-protocol/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ pub enum ContractError {
// Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details.
#[error("Only a single asset is allowed")]
OnlySingleAssetAllowed {},

#[error("Asset not whitelisted")]
AssetNotWhitelisted {},
#[error("Asset '{0}' not whitelisted")]
AssetNotWhitelisted(String),

#[error("Insufficient balance")]
InsufficientBalance {},
Expand Down

0 comments on commit 9e9729c

Please sign in to comment.