Skip to content

Commit

Permalink
wip: claim rewards
Browse files Browse the repository at this point in the history
  • Loading branch information
emidev98 committed Feb 10, 2024
1 parent 103203c commit 6a3cc5d
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 45 deletions.
69 changes: 34 additions & 35 deletions contracts/alliance-lp-hub/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ pub fn execute(
}
}

// Method that allows execution only though the governance address.
// Function that allows execution only though the governance address.
// It has two types of executions,
// - first execution type is to remove assets from the whitelist defined by asset.delete,
// - second execution type is to overwrite the asset in the whitelist with the new asset_distribution,
Expand Down Expand Up @@ -173,7 +173,7 @@ fn modify_asset(
Ok(Response::new().add_attributes(attrs))
}

// This method is used to stake both native and CW20 tokens,
// This function is used to stake both native and CW20 tokens,
// it checks if the asset is whitelisted, if it is it will
// claim rewards and check if the asset is whitelisted in astro
// incentives update the balances incrementing both the
Expand All @@ -189,25 +189,31 @@ fn stake(deps: DepsMut, sender: Addr, received_asset: Asset) -> Result<Response,
for f in rewards_list.into_iter() {
let key = (
sender.clone(),
AssetInfoKey::from(f.deposit_asset.unwrap().clone()),
AssetInfoKey::from(f.reward_asset.unwrap().clone()),
AssetInfoKey::from(f.deposit_asset.clone().unwrap()),
AssetInfoKey::from(f.reward_asset.clone().unwrap()),
);

// If the rewards are zero, it means that the asset is
// staked for the first time or that there were no rewards
// distributed to this asset, so to make it easy on future
// operations we assign the total asset reward rate to the
// user asset reward rate even if both values are the same.
if f.rewards.is_zero() {
let asset_reward_rate =
TOTAL_ASSET_REWARD_RATE.load(deps.storage, (key.1.clone(), key.2.clone()))?;
USER_ASSET_REWARD_RATE.save(deps.storage, key.clone(), &asset_reward_rate)?;
} else {
let unclaimed_rewards = UNCLAIMED_REWARDS
.load(deps.storage, key.clone())
.unwrap_or_default();
let final_rewards = f.rewards + unclaimed_rewards;
UNCLAIMED_REWARDS.save(deps.storage, key, &final_rewards)?
// When rewards are not zero, it means that the user has unclaimed
// rewards, so we keep track of them in the UNCLAIMED_REWARDS map.
UNCLAIMED_REWARDS.update(
deps.storage,
key.clone(),
|b| -> Result<_, ContractError> { Ok(b.unwrap_or_default() + f.rewards) },
)?;
}
}

// Query astro incentives removing the AssetInfo prefix to transform it from
// "cw20:terra11s4" to "terra11s4" or "native:uluna" -> "uluna"
// Query astro incentives contract to check if the asset is whitelisted
let config = CONFIG.load(deps.storage)?;
let astro_incentives: Vec<RewardInfo> = deps
.querier
Expand All @@ -226,10 +232,9 @@ fn stake(deps: DepsMut, sender: Addr, received_asset: Asset) -> Result<Response,
("amount", &received_asset.amount.to_string()),
]);

// When Astro incentives is not empty it means that the asset
// When Astro incentives list is not empty it means that the asset
// is whitelisted in astro incentives, so we need to send the
// tokens to the astro incentives contract and then stake them.
// Besides that we also claim rewards from astro incentives if any
if !astro_incentives.is_empty() {
let msg = _create_astro_deposit_msg(
received_asset.clone(),
Expand Down Expand Up @@ -423,48 +428,39 @@ fn unstake_callback(
.add_message(asset.transfer_msg(usr)?))
}

// Claiming rewards process iterates over the array of rewards
// and for each reward it will check if the user has any unclaimed
// rewards, if it does it will sum the unclaimed rewards with the
// rewards from the current iteration and send the tokens to the user.
fn claim_rewards(
deps: DepsMut,
info: MessageInfo,
asset: AssetInfo,
deposit_asset: AssetInfo,
) -> Result<Response, ContractError> {
let sender = info.sender;
let mut res = Response::new()
.add_attribute("action", "claim_alliance_lp_rewards")
.add_attribute("sender", sender.as_ref());

let rewards_list = _claim_rewards(&deps, sender.clone(), asset.clone())?;
let rewards_list = _claim_rewards(&deps, sender.clone(), deposit_asset.clone())?;
for f in rewards_list.into_iter() {
let deposit_asset_key = AssetInfoKey::from(f.deposit_asset.clone().unwrap());
let reward_asset_key = AssetInfoKey::from(f.reward_asset.clone().unwrap());
let key = (sender.clone(), deposit_asset_key, reward_asset_key);

if f.rewards.is_zero() {
let asset_reward_rate =
TOTAL_ASSET_REWARD_RATE.load(deps.storage, (key.1.clone(), key.2.clone()))?;
USER_ASSET_REWARD_RATE.save(deps.storage, key.clone(), &asset_reward_rate)?;
} else {
let unclaimed_rewards = UNCLAIMED_REWARDS
.load(deps.storage, key.clone())
.unwrap_or_default();
let final_rewards = f.rewards + unclaimed_rewards;
UNCLAIMED_REWARDS.save(deps.storage, key.clone(), &final_rewards)?;
}

let unclaimed_rewards = UNCLAIMED_REWARDS
.load(deps.storage, key.clone())
.unwrap_or_default();
UNCLAIMED_REWARDS.remove(deps.storage, key);

let final_rewards = f.rewards.clone() + unclaimed_rewards;

let final_rewards = f.rewards + unclaimed_rewards;
if !final_rewards.is_zero() {
let rewards_asset = Asset {
info: f.reward_asset.clone().unwrap(),
amount: final_rewards,
};
res = res
.add_attribute("deposit_asset", &asset.to_string())
.add_attribute("deposit_asset", &deposit_asset.to_string())
.add_attribute("reward_asset", rewards_asset.info.to_string())
.add_attribute("rewards_amount", &final_rewards.to_string())
.add_message(rewards_asset.transfer_msg(&sender)?)
Expand All @@ -485,7 +481,7 @@ fn _claim_rewards(
.unwrap_or_default();
let user_balance = Decimal::from_atomics(user_balance, 0)?;

TOTAL_ASSET_REWARD_RATE
let claimed_rewards = TOTAL_ASSET_REWARD_RATE
.prefix(deposit_asset_key.clone())
.range(deps.storage, None, None, Order::Ascending)
.map(|f| {
Expand Down Expand Up @@ -527,7 +523,9 @@ fn _claim_rewards(
Uint128::zero(),
))
})
.collect()
.collect();

claimed_rewards
}

fn alliance_delegate(
Expand Down Expand Up @@ -752,6 +750,10 @@ fn update_rewards(deps: DepsMut, env: Env, info: MessageInfo) -> Result<Response
Ok(res.add_submessages(alliance_sub_msg).add_message(msg))
}

// This function only deals with alliance rewards. Other rewards (e.g. ASTRO)
// needs to be dealt with separately This is because the reward distribution
// only affects alliance rewards. LP rewards are directly distributed to LP
// holders and not pooled together and shared
fn update_alliance_reward_callback(
deps: DepsMut,
env: Env,
Expand All @@ -763,9 +765,6 @@ fn update_alliance_reward_callback(
let config = CONFIG.load(deps.storage)?;
let mut res =
Response::new().add_attributes(vec![("action", "update_alliance_rewards_callback")]);
// We only deal with alliance rewards here. Other rewards (e.g. ASTRO) needs to be dealt with separately
// This is because the reward distribution only affects alliance rewards. LP rewards are directly distributed to LP holders
// and not pooled together and shared
let reward_asset_info_key = AssetInfoKey::from(config.alliance_reward_denom.clone());
let current_balance = config
.alliance_reward_denom
Expand Down
56 changes: 46 additions & 10 deletions contracts/alliance-lp-hub/src/tests/rewards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,10 @@ fn claim_user_rewards_after_staking() {
let mut deps = mock_dependencies(Some(&[coin(2000000, "uluna")]));
setup_contract(deps.as_mut());
set_alliance_asset(deps.as_mut());
modify_asset(

// Whitelist two assets with 50% of asset distribution
// from the alliance staking to each asset
let res = modify_asset(
deps.as_mut(),
Vec::from([
ModifyAssetPair {
Expand All @@ -512,8 +515,31 @@ fn claim_user_rewards_after_staking() {
]),
)
.unwrap();
stake(deps.as_mut(), "user1", 1000000, "aWHALE").unwrap();
stake(deps.as_mut(), "user2", 4000000, "aWHALE").unwrap();
assert_eq!(res, Response::new()
.add_attribute("action", "modify_asset")
.add_attribute("asset", "native:aWHALE")
.add_attribute("asset", "native:bWHALE"));

let res = stake(deps.as_mut(), "user1", 1000000, "aWHALE").unwrap();
assert_eq!(
res,
Response::new().add_attributes(vec![
("action", "stake"),
("user", "user1"),
("asset", "native:aWHALE"),
("amount", "1000000"),
])
);
let res = stake(deps.as_mut(), "user2", 4000000, "aWHALE").unwrap();
assert_eq!(
res,
Response::new().add_attributes(vec![
("action", "stake"),
("user", "user2"),
("asset", "native:aWHALE"),
("amount", "4000000"),
])
);

TEMP_BALANCE
.save(
Expand All @@ -522,13 +548,23 @@ fn claim_user_rewards_after_staking() {
&Uint128::new(1000000),
)
.unwrap();
execute(

let res = execute(
deps.as_mut(),
mock_env(),
mock_info("cosmos2contract", &[]),
ExecuteMsg::UpdateAllianceRewardsCallback {},
)
.unwrap();
assert_eq!(
res,
Response::new()
.add_attribute("action", "update_alliance_rewards_callback")
.add_message(CosmosMsg::Bank(BankMsg::Send {
to_address: "controller".to_string(),
amount: coins(1000000, "uluna"),
}))
);

stake(deps.as_mut(), "user1", 1000000, "aWHALE").unwrap();

Expand All @@ -539,9 +575,9 @@ fn claim_user_rewards_after_staking() {
.add_attributes(vec![
("action", "claim_alliance_lp_rewards"),
("user", "user1"),
("asset", "native:aWHALE"),
("alliance_reward_amount", "100000"),
("astro_reward_amount", "0"),
("deposit_asset", "native:aWHALE"),
("reward_asset", "100000"),
("rewards_amount", "0"),
])
.add_message(CosmosMsg::Bank(BankMsg::Send {
to_address: "user1".to_string(),
Expand All @@ -556,9 +592,9 @@ fn claim_user_rewards_after_staking() {
Response::new().add_attributes(vec![
("action", "claim_alliance_lp_rewards"),
("user", "user1"),
("asset", "native:aWHALE"),
("alliance_reward_amount", "0"),
("astro_reward_amount", "0"),
("deposit_asset", "native:aWHALE"),
("reward_asset", "0"),
("rewards_amount", "0"),
])
);
}
Expand Down

0 comments on commit 6a3cc5d

Please sign in to comment.