diff --git a/contracts/liquidity_hub/bonding-manager/schema/bonding-manager.json b/contracts/liquidity_hub/bonding-manager/schema/bonding-manager.json index 7951283d..c731c146 100644 --- a/contracts/liquidity_hub/bonding-manager/schema/bonding-manager.json +++ b/contracts/liquidity_hub/bonding-manager/schema/bonding-manager.json @@ -98,7 +98,7 @@ "additionalProperties": false }, { - "description": "Sends withdrawable unbonded tokens to the user.", + "description": "Sends withdrawable assets of the given denom to the user. An asset becomes withdrawable after it has been unbonded and the unbonding period has passed.", "type": "object", "required": [ "withdraw" diff --git a/contracts/liquidity_hub/bonding-manager/schema/raw/execute.json b/contracts/liquidity_hub/bonding-manager/schema/raw/execute.json index 4df3f63b..78bebfa4 100644 --- a/contracts/liquidity_hub/bonding-manager/schema/raw/execute.json +++ b/contracts/liquidity_hub/bonding-manager/schema/raw/execute.json @@ -37,7 +37,7 @@ "additionalProperties": false }, { - "description": "Sends withdrawable unbonded tokens to the user.", + "description": "Sends withdrawable assets of the given denom to the user. An asset becomes withdrawable after it has been unbonded and the unbonding period has passed.", "type": "object", "required": [ "withdraw" diff --git a/contracts/liquidity_hub/bonding-manager/src/bonding/commands.rs b/contracts/liquidity_hub/bonding-manager/src/bonding/commands.rs index c93c7e1a..ca4a1dd5 100644 --- a/contracts/liquidity_hub/bonding-manager/src/bonding/commands.rs +++ b/contracts/liquidity_hub/bonding-manager/src/bonding/commands.rs @@ -84,6 +84,11 @@ pub(crate) fn bond( GLOBAL.save(deps.storage, &global_index)?; + // first time the user bonds it shouldn't be able to claim rewards until the next epoch. This is + // why we save the last claimed epoch as the current epoch. + // In case the user has already bonded before, it won't be able to bond again without first + // claiming the pending rewards, in which case the last claimed epoch will be updated to the + // current epoch anyway. LAST_CLAIMED_EPOCH.save(deps.storage, &info.sender, ¤t_epoch.epoch.id)?; Ok(Response::default().add_attributes(vec![ @@ -125,12 +130,6 @@ pub(crate) fn unbond( if bonds_by_receiver.is_empty() { Err(ContractError::NothingToUnbond) } else { - // sanity check - ensure!( - bonds_by_receiver.len() == 1usize, - ContractError::AssetMismatch - ); - let mut unbond = bonds_by_receiver[0].clone(); // check if the address has enough bond @@ -192,7 +191,7 @@ pub(crate) fn unbond( } } -/// Withdraws the rewards for the provided address +/// Withdraws the unbonded asset of the given denom for the provided address pub(crate) fn withdraw( deps: DepsMut, address: Addr, diff --git a/contracts/liquidity_hub/bonding-manager/src/helpers.rs b/contracts/liquidity_hub/bonding-manager/src/helpers.rs index 2b4d444b..83711b03 100644 --- a/contracts/liquidity_hub/bonding-manager/src/helpers.rs +++ b/contracts/liquidity_hub/bonding-manager/src/helpers.rs @@ -165,7 +165,7 @@ pub fn swap_coins_to_main_token( coins: Vec, deps: &DepsMut, config: Config, - to_be_distribution_asset: &mut Coin, + distribution_asset: &mut Coin, distribution_denom: &String, messages: &mut Vec, ) -> Result<(), ContractError> { @@ -233,7 +233,7 @@ pub fn swap_coins_to_main_token( // Add the simulate amount received to the distribution_denom amount, if the swap fails this should // also be rolled back - to_be_distribution_asset.amount = to_be_distribution_asset + distribution_asset.amount = distribution_asset .amount .checked_add(simulate_swap_operations_response.amount)?; diff --git a/contracts/liquidity_hub/bonding-manager/src/rewards/commands.rs b/contracts/liquidity_hub/bonding-manager/src/rewards/commands.rs index ae0ad7f9..6b7c7082 100644 --- a/contracts/liquidity_hub/bonding-manager/src/rewards/commands.rs +++ b/contracts/liquidity_hub/bonding-manager/src/rewards/commands.rs @@ -29,19 +29,16 @@ pub(crate) fn on_epoch_created( ContractError::Unauthorized ); - let global = GLOBAL.may_load(deps.storage)?; - // This happens only on the very first epoch where Global has not been initialised yet - if global.is_none() { - let initial_global_index = GlobalIndex { + let mut global_index = GLOBAL.load(deps.storage).unwrap_or( + // This happens only on the very first epoch where Global has not been initialised yet + GlobalIndex { epoch_id: current_epoch.id, last_updated: current_epoch.id, ..Default::default() - }; - GLOBAL.save(deps.storage, &initial_global_index)?; - } + }, + ); // Update the global index epoch id - let mut global_index = GLOBAL.load(deps.storage)?; global_index.epoch_id = current_epoch.id; if global_index.bonded_amount == Uint128::zero() { @@ -100,7 +97,7 @@ pub(crate) fn fill_rewards( let mut submessages: Vec = vec![]; // swap non-whale to whale - let mut distribution_denom_in_tx = info + let mut distribution_asset_in_tx = info .funds .iter() .find(|coin| coin.denom.eq(distribution_denom.as_str())) @@ -126,7 +123,7 @@ pub(crate) fn fill_rewards( remnant_coins, &deps, config, - &mut distribution_denom_in_tx, + &mut distribution_asset_in_tx, &distribution_denom, &mut messages, )?; @@ -136,7 +133,7 @@ pub(crate) fn fill_rewards( // Because we are using minimum receive, it is possible the contract can accumulate micro amounts of whale if we get more than what the swap query returned // If this became an issue we could look at replies instead of the query // The lp_tokens being withdrawn are handled in the reply entry point - fill_upcoming_reward_bucket(deps, distribution_denom_in_tx.clone())?; + fill_upcoming_reward_bucket(deps, distribution_asset_in_tx.clone())?; Ok(Response::default() .add_messages(messages) diff --git a/contracts/liquidity_hub/bonding-manager/src/state.rs b/contracts/liquidity_hub/bonding-manager/src/state.rs index 738de7c5..a085f1f3 100644 --- a/contracts/liquidity_hub/bonding-manager/src/state.rs +++ b/contracts/liquidity_hub/bonding-manager/src/state.rs @@ -11,7 +11,7 @@ pub const BONDING_ASSETS_LIMIT: usize = 2; pub const CONFIG: Item = Item::new("config"); /// A monotonically increasing counter to generate unique bond ids. -pub const BOND_COUNTER: Item = Item::new("bond_id_counter"); +pub const BOND_COUNTER: Item = Item::new("bond_counter"); pub const BONDS: IndexedMap = IndexedMap::new( "bonds", BondIndexes { diff --git a/contracts/liquidity_hub/epoch-manager/tests/epoch.rs b/contracts/liquidity_hub/epoch-manager/tests/epoch.rs index 03e6d5e2..6bcb8f78 100644 --- a/contracts/liquidity_hub/epoch-manager/tests/epoch.rs +++ b/contracts/liquidity_hub/epoch-manager/tests/epoch.rs @@ -2,6 +2,7 @@ use cosmwasm_std::from_json; use cosmwasm_std::testing::{mock_env, mock_info}; use epoch_manager::contract::{execute, query}; +use epoch_manager::ContractError; use white_whale_std::epoch_manager::epoch_manager::{Epoch, EpochResponse, ExecuteMsg, QueryMsg}; use white_whale_std::epoch_manager::hooks::EpochChangedHookMsg; use white_whale_std::pool_network::mock_querier::mock_dependencies; @@ -61,20 +62,20 @@ fn create_new_epoch_successfully() { ); } -// #[test] -// fn create_new_epoch_unsuccessfully() { -// let mut deps = mock_dependencies(&[]); -// let info = mock_info("owner", &[]); -// let mut env = mock_env(); -// mock_instantiation(deps.as_mut(), info.clone()).unwrap(); - -// // move time ahead but not enough so the epoch creation fails -// env.block.time = env.block.time.plus_nanos(86300); - -// let msg = ExecuteMsg::CreateEpoch; -// let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); -// match err { -// ContractError::CurrentEpochNotExpired => {} -// _ => panic!("should return ContractError::CurrentEpochNotExpired"), -// } -// } +#[test] +fn create_new_epoch_unsuccessfully() { + let mut deps = mock_dependencies(&[]); + let info = mock_info("owner", &[]); + let mut env = mock_env(); + mock_instantiation(deps.as_mut(), info.clone()).unwrap(); + + // move time ahead but not enough so the epoch creation fails + env.block.time = env.block.time.plus_nanos(86300); + + let msg = ExecuteMsg::CreateEpoch; + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + match err { + ContractError::CurrentEpochNotExpired => {} + _ => panic!("should return ContractError::CurrentEpochNotExpired"), + } +} diff --git a/contracts/liquidity_hub/incentive-manager/src/state.rs b/contracts/liquidity_hub/incentive-manager/src/state.rs index 245d539f..a27f0378 100644 --- a/contracts/liquidity_hub/incentive-manager/src/state.rs +++ b/contracts/liquidity_hub/incentive-manager/src/state.rs @@ -44,8 +44,7 @@ pub const LAST_CLAIMED_EPOCH: Map<&Addr, EpochId> = Map::new("last_claimed_epoch /// The lp weight history for addresses, including the contract. i.e. how much lp weight an address /// or contract has at a given epoch. /// Key is a tuple of (address, lp_denom, epoch_id), value is the lp weight. -pub const LP_WEIGHT_HISTORY: Map<(&Addr, &str, EpochId), Uint128> = - Map::new("address_lp_weight_history"); +pub const LP_WEIGHT_HISTORY: Map<(&Addr, &str, EpochId), Uint128> = Map::new("lp_weight_history"); /// A monotonically increasing counter to generate unique incentive identifiers. pub const INCENTIVE_COUNTER: Item = Item::new("incentive_counter"); diff --git a/packages/white-whale-std/src/bonding_manager.rs b/packages/white-whale-std/src/bonding_manager.rs index 0259919a..535b0ce7 100644 --- a/packages/white-whale-std/src/bonding_manager.rs +++ b/packages/white-whale-std/src/bonding_manager.rs @@ -135,7 +135,8 @@ pub enum ExecuteMsg { /// The asset to unbond. asset: Coin, }, - /// Sends withdrawable unbonded tokens to the user. + /// Sends withdrawable assets of the given denom to the user. An asset becomes withdrawable after + /// it has been unbonded and the unbonding period has passed. Withdraw { /// The denom to withdraw. denom: String,