Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(bonding_manager): fix validation of epoch when bonding/unbonding #371

Merged
merged 5 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,28 @@
"claim"
]
},
{
"description": "Claims the available rewards on behalf of the specified address. Only executable by the contract.",
"type": "object",
"required": [
"claim_for_addr"
],
"properties": {
"claim_for_addr": {
"type": "object",
"required": [
"address"
],
"properties": {
"address": {
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"description": "Fills the contract with new rewards.",
"type": "string",
Expand Down
22 changes: 22 additions & 0 deletions contracts/liquidity_hub/bonding-manager/schema/raw/execute.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,28 @@
"claim"
]
},
{
"description": "Claims the available rewards on behalf of the specified address. Only executable by the contract.",
"type": "object",
"required": [
"claim_for_addr"
],
"properties": {
"claim_for_addr": {
"type": "object",
"required": [
"address"
],
"properties": {
"address": {
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"description": "Fills the contract with new rewards.",
"type": "string",
Expand Down
75 changes: 63 additions & 12 deletions contracts/liquidity_hub/bonding-manager/src/bonding/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ use cosmwasm_std::{
Uint128,
};

use white_whale_std::bonding_manager::Bond;
use white_whale_std::bonding_manager::{Bond, BondAction, Config, TemporalBondAction};
use white_whale_std::pool_network::asset;

use crate::helpers::temporal_bond_action_response;
use crate::state::{
get_bonds_by_receiver, update_bond_weight, update_global_weight, BONDS, BOND_COUNTER, CONFIG,
GLOBAL, LAST_CLAIMED_EPOCH, MAX_LIMIT,
Expand All @@ -15,15 +16,19 @@ use crate::{helpers, ContractError};
/// Bonds the provided asset.
pub(crate) fn bond(
mut deps: DepsMut,
info: MessageInfo,
_env: Env,
asset: Coin,
info: &MessageInfo,
env: Env,
asset: &Coin,
) -> Result<Response, ContractError> {
helpers::validate_buckets_not_empty(&deps)?;
helpers::validate_claimed(&deps, &info)?;
helpers::validate_bonding_for_current_epoch(&deps)?;

let config = CONFIG.load(deps.storage)?;

if let Some(temporal_bond_action_response) =
validate_bond_operation(&mut deps, info, &env, asset, &config, BondAction::Bond)
{
return temporal_bond_action_response;
}

let current_epoch: white_whale_std::epoch_manager::epoch_manager::EpochResponse =
deps.querier.query_wasm_smart(
config.epoch_manager_addr,
Expand Down Expand Up @@ -101,17 +106,22 @@ pub(crate) fn bond(
/// Unbonds the provided amount of tokens
pub(crate) fn unbond(
mut deps: DepsMut,
info: MessageInfo,
info: &MessageInfo,
env: Env,
asset: Coin,
asset: &Coin,
) -> Result<Response, ContractError> {
ensure!(
asset.amount > Uint128::zero(),
ContractError::InvalidUnbondingAmount
);

helpers::validate_claimed(&deps, &info)?;
helpers::validate_bonding_for_current_epoch(&deps)?;
let config = CONFIG.load(deps.storage)?;

if let Some(temporal_bond_action_response) =
validate_bond_operation(&mut deps, info, &env, asset, &config, BondAction::Unbond)
{
return temporal_bond_action_response;
}

let bonds_by_receiver = get_bonds_by_receiver(
deps.storage,
Expand All @@ -138,7 +148,6 @@ pub(crate) fn unbond(
ContractError::InsufficientBond
);

let config = CONFIG.load(deps.storage)?;
let current_epoch: white_whale_std::epoch_manager::epoch_manager::EpochResponse =
deps.querier.query_wasm_smart(
config.epoch_manager_addr,
Expand Down Expand Up @@ -242,3 +251,45 @@ pub(crate) fn withdraw(
("refund_amount", refund_amount.to_string()),
]))
}

/// Validates the bond operation. Makes sure the user has claimed pending rewards and that the current
/// epoch is valid. If any of these operations fail, the contract will resolve them by triggering
/// the claim rewards operation on behalf of the user, or by sending a message to the epoch manager
/// to create a new epoch.
///
/// Used during both Bond and Unbond.
fn validate_bond_operation(
deps: &mut DepsMut,
info: &MessageInfo,
env: &Env,
asset: &Coin,
config: &Config,
bond_action: BondAction,
) -> Option<Result<Response, ContractError>> {
if helpers::validate_claimed(deps, info).is_err() {
return Some(temporal_bond_action_response(
deps,
&env.contract.address,
TemporalBondAction {
sender: info.sender.clone(),
coin: asset.clone(),
action: bond_action,
},
ContractError::UnclaimedRewards,
));
}

if helpers::validate_bonding_for_current_epoch(deps, env).is_err() {
return Some(temporal_bond_action_response(
deps,
&config.epoch_manager_addr,
TemporalBondAction {
sender: info.sender.clone(),
coin: asset.clone(),
action: bond_action,
},
ContractError::EpochNotCreatedYet,
));
}
None
}
72 changes: 63 additions & 9 deletions contracts/liquidity_hub/bonding-manager/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
use crate::error::ContractError;
use crate::helpers::{self, validate_growth_rate};
use crate::state::{BONDING_ASSETS_LIMIT, BOND_COUNTER, CONFIG, UPCOMING_REWARD_BUCKET};
use crate::{bonding, commands, queries, rewards};
use cosmwasm_std::{ensure, entry_point, Addr, Reply};
use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response};
use cw2::{get_contract_version, set_contract_version};

use white_whale_std::bonding_manager::{
Config, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, UpcomingRewardBucket,
BondAction, Config, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, TemporalBondAction,
UpcomingRewardBucket,
};

use crate::error::ContractError;
use crate::helpers::{self, validate_growth_rate};
use crate::state::{
BONDING_ASSETS_LIMIT, BOND_COUNTER, CONFIG, TMP_BOND_ACTION, UPCOMING_REWARD_BUCKET,
};
use crate::{bonding, commands, queries, rewards};

// version info for migration info
const CONTRACT_NAME: &str = "white_whale-bonding_manager";
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

pub const LP_WITHDRAWAL_REPLY_ID: u64 = 0;
pub const NEW_EPOCH_CREATION_REPLY_ID: u64 = 1;

#[entry_point]
pub fn instantiate(
Expand Down Expand Up @@ -59,9 +65,39 @@

// Reply entrypoint handling LP withdraws from fill_rewards
#[entry_point]
pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result<Response, ContractError> {
pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result<Response, ContractError> {
match msg.id {
LP_WITHDRAWAL_REPLY_ID => rewards::commands::handle_lp_withdrawal_reply(deps, msg),
NEW_EPOCH_CREATION_REPLY_ID => {
let TemporalBondAction {
sender,
coin,
action,
} = TMP_BOND_ACTION.load(deps.storage)?;

TMP_BOND_ACTION.remove(deps.storage);

match action {
BondAction::Bond => bonding::commands::bond(
deps,
&MessageInfo {
sender,
funds: vec![coin.clone()],
},
env,
&coin,
),
BondAction::Unbond => bonding::commands::unbond(
deps,
&MessageInfo {
sender,
funds: vec![],
},
env,
&coin,
),
}
}
_ => Err(ContractError::Unauthorized),
}
}
Expand All @@ -76,11 +112,11 @@
match msg {
ExecuteMsg::Bond => {
let asset_to_bond = helpers::validate_funds(&deps, &info)?;
bonding::commands::bond(deps, info, env, asset_to_bond)
bonding::commands::bond(deps, &info, env, &asset_to_bond)
}
ExecuteMsg::Unbond { asset } => {
cw_utils::nonpayable(&info)?;
bonding::commands::unbond(deps, info, env, asset)
bonding::commands::unbond(deps, &info, env, &asset)
}
ExecuteMsg::Withdraw { denom } => {
cw_utils::nonpayable(&info)?;
Expand All @@ -103,7 +139,25 @@
)
}
ExecuteMsg::FillRewards => rewards::commands::fill_rewards(deps, env, info),
ExecuteMsg::Claim => rewards::commands::claim(deps, info),
ExecuteMsg::Claim => {
cw_utils::nonpayable(&info)?;
rewards::commands::claim(deps, info)
}
ExecuteMsg::ClaimForAddr { address } => {
cw_utils::nonpayable(&info)?;
ensure!(
info.sender == env.contract.address,
ContractError::Unauthorized

Check warning on line 150 in contracts/liquidity_hub/bonding-manager/src/contract.rs

View check run for this annotation

Codecov / codecov/patch

contracts/liquidity_hub/bonding-manager/src/contract.rs#L150

Added line #L150 was not covered by tests
);
let sender = deps.api.addr_validate(&address)?;
rewards::commands::claim(
deps,
MessageInfo {
sender,
funds: vec![],
},
)
}
ExecuteMsg::EpochChangedHook { current_epoch } => {
rewards::commands::on_epoch_created(deps, info, current_epoch)
}
Expand Down
Loading
Loading