diff --git a/contracts/liquidity_hub/bonding-manager/src/commands.rs b/contracts/liquidity_hub/bonding-manager/src/commands.rs index 7432d1dd..7c207bec 100644 --- a/contracts/liquidity_hub/bonding-manager/src/commands.rs +++ b/contracts/liquidity_hub/bonding-manager/src/commands.rs @@ -227,15 +227,17 @@ pub fn claim(deps: DepsMut, env: Env, info: MessageInfo) -> Result StdResult<_> { let mut bucket = bucket.unwrap_or_default(); - bucket.available = asset::aggregate_coins(bucket.available, vec![])?; + bucket.available = asset::aggregate_coins( + bucket.available, + vec![Coin { + denom: "uwhale".to_string(), + amount: Uint128::new(1000u128), + }], + )?; Ok(bucket) }, )?; diff --git a/contracts/liquidity_hub/bonding-manager/src/contract.rs b/contracts/liquidity_hub/bonding-manager/src/contract.rs index b4dfb692..71ca893d 100644 --- a/contracts/liquidity_hub/bonding-manager/src/contract.rs +++ b/contracts/liquidity_hub/bonding-manager/src/contract.rs @@ -144,6 +144,17 @@ pub fn execute( println!("New epoch created: {}", next_epoch_id); // Return early if the epoch is the first one if new_epoch_id == 1 { + // Creates a new bucket for the rewards flowing from this time on, i.e. to be distributed in the next epoch. Also, forwards the expiring epoch (only 21 epochs are live at a given moment) + // Add a new rewards bucket for the new epoch + EPOCHS.save( + deps.storage, + &new_epoch_id.to_be_bytes(), + &Epoch { + id: next_epoch_id.into(), + start_time: current_epoch.start_time, + ..Epoch::default() + }, + )?; return Ok(Response::default() .add_attributes(vec![("action", "epoch_changed_hook".to_string())])); } @@ -155,6 +166,7 @@ pub fn execute( Some(_) => Err(ContractError::Unauthorized {}), None => Err(ContractError::Unauthorized {}), // Handle the case where there is no expiring epoch }; + println!("New epoch created: {}", next_epoch_id); // Creates a new bucket for the rewards flowing from this time on, i.e. to be distributed in the next epoch. Also, forwards the expiring epoch (only 21 epochs are live at a given moment) // Add a new rewards bucket for the new epoch @@ -172,6 +184,7 @@ pub fn execute( let amount_to_be_forwarded = EPOCHS .load(deps.storage, &expiring_epoch_id.to_be_bytes())? .available; + println!("Amount to be forwarded: {:?}", amount_to_be_forwarded); EPOCHS.update( deps.storage, &new_epoch_id.to_be_bytes(), diff --git a/contracts/liquidity_hub/bonding-manager/src/queries.rs b/contracts/liquidity_hub/bonding-manager/src/queries.rs index e293e99f..f13a41cd 100644 --- a/contracts/liquidity_hub/bonding-manager/src/queries.rs +++ b/contracts/liquidity_hub/bonding-manager/src/queries.rs @@ -316,7 +316,7 @@ pub fn query_claimable(deps: Deps, address: &Addr) -> StdResult bonded_response.first_bonded_epoch_id); } }; - + println!("claimable_epochs: {:?}", claimable_epochs); // filter out epochs that have no available fees. This would only happen in case the grace period // gets increased after epochs have expired, which would lead to make them available for claiming // again without any available rewards, as those were forwarded to newer epochs. diff --git a/contracts/liquidity_hub/bonding-manager/src/tests/claim.rs b/contracts/liquidity_hub/bonding-manager/src/tests/claim.rs index 174789f3..223f35e7 100644 --- a/contracts/liquidity_hub/bonding-manager/src/tests/claim.rs +++ b/contracts/liquidity_hub/bonding-manager/src/tests/claim.rs @@ -1,8 +1,14 @@ -use cosmwasm_std::testing::{mock_dependencies, mock_env}; -use cosmwasm_std::Uint64; +use cosmwasm_std::{coin, Uint64}; +use white_whale_std::fee::{Fee, PoolFee}; +use white_whale_std::pool_network::asset::MINIMUM_LIQUIDITY_AMOUNT; use crate::tests::robot::TestingRobot; use crate::tests::test_helpers; +use cosmwasm_std::{coins, Coin, Decimal, Timestamp, Uint128}; + +use white_whale_std::bonding_manager::{BondedResponse, BondingWeightResponse}; + +use super::test_helpers::get_epochs; #[test] fn test_claimable_epochs() { @@ -29,3 +35,211 @@ fn test_claimable_epochs() { } }); } + +#[test] +fn test_bond_successfully() { + let mut robot = TestingRobot::default(); + let sender = robot.sender.clone(); + let another_sender = robot.another_sender.clone(); + let asset_infos = vec!["uwhale".to_string(), "uusdc".to_string()]; + + // Default Pool fees white_whale_std::pool_network::pair::PoolFee + // Protocol fee is 0.01% and swap fee is 0.02% and burn fee is 0% + #[cfg(not(feature = "osmosis"))] + let pool_fees = PoolFee { + protocol_fee: Fee { + share: Decimal::from_ratio(1u128, 100u128), + }, + swap_fee: Fee { + share: Decimal::from_ratio(1u128, 100u128), + }, + burn_fee: Fee { + share: Decimal::zero(), + }, + extra_fees: vec![], + }; + get_epochs(); + robot + .instantiate_default() + .bond( + sender.clone(), + Coin { + denom: "ampWHALE".to_string(), + amount: Uint128::new(1_000u128), + }, + &coins(1_000u128, "ampWHALE"), + |_res| {}, + ) + .assert_bonded_response( + sender.to_string(), + BondedResponse { + total_bonded: Uint128::new(1_000u128), + bonded_assets: vec![Coin { + denom: "ampWHALE".to_string(), + amount: Uint128::new(1_000u128), + }], + first_bonded_epoch_id: Default::default(), + }, + ) + .fast_forward(10u64) + .assert_bonding_weight_response( + sender.to_string(), + BondingWeightResponse { + address: sender.to_string(), + weight: Uint128::new(11_000u128), + global_weight: Uint128::new(11_000u128), + share: Decimal::one(), + timestamp: Timestamp::from_nanos(1571797429879305533u64), + }, + ) + .fast_forward(10u64) + .bond( + sender.clone(), + Coin { + denom: "bWHALE".to_string(), + amount: Uint128::new(3_000u128), + }, + &coins(3_000u128, "bWHALE"), + |_res| {}, + ) + .assert_bonded_response( + sender.to_string(), + BondedResponse { + total_bonded: Uint128::new(4_000u128), + bonded_assets: vec![ + Coin { + denom: "ampWHALE".to_string(), + amount: Uint128::new(1_000u128), + }, + Coin { + denom: "bWHALE".to_string(), + amount: Uint128::new(3_000u128), + }, + ], + first_bonded_epoch_id: Default::default(), + }, + ) + .fast_forward(10u64) + .bond( + another_sender.clone(), + Coin { + denom: "ampWHALE".to_string(), + amount: Uint128::new(5_000u128), + }, + &coins(5_000u128, "ampWHALE"), + |_res| {}, + ) + .fast_forward(10u64) + .assert_bonding_weight_response( + sender.to_string(), + BondingWeightResponse { + address: sender.to_string(), + weight: Uint128::new(104_000u128), + global_weight: Uint128::new(269_000u128), + share: Decimal::from_ratio(104_000u128, 269_000u128), + timestamp: Timestamp::from_nanos(1571797459879305533u64), + }, + ) + .assert_bonding_weight_response( + another_sender.to_string(), + BondingWeightResponse { + address: another_sender.to_string(), + weight: Uint128::new(55_000u128), + global_weight: Uint128::new(269_000u128), + share: Decimal::from_ratio(55_000u128, 269_000u128), + timestamp: Timestamp::from_nanos(1571797459879305533u64), + }, + ) + .query_total_bonded(|res| { + let bonded_response = res.unwrap().1; + assert_eq!( + bonded_response, + BondedResponse { + total_bonded: Uint128::new(9_000u128), + bonded_assets: vec![ + Coin { + denom: "ampWHALE".to_string(), + amount: Uint128::new(6_000u128), + }, + Coin { + denom: "bWHALE".to_string(), + amount: Uint128::new(3_000u128), + }, + ], + first_bonded_epoch_id: Default::default(), + } + ) + }); + + robot.query_claimable_epochs_live(Some(sender.clone()), |res| { + let (_, epochs) = res.unwrap(); + assert_eq!(epochs.len(), 0); + }); + + robot.create_pair( + sender.clone(), + asset_infos.clone(), + pool_fees.clone(), + white_whale_std::pool_network::asset::PairType::ConstantProduct, + Some("whale-uusdc".to_string()), + vec![coin(1000, "uusdc")], + |result| { + result.unwrap(); + }, + ); + + // Lets try to add liquidity + robot.provide_liquidity( + sender.clone(), + "whale-uusdc".to_string(), + vec![ + Coin { + denom: "uwhale".to_string(), + amount: Uint128::from(1000000000u128), + }, + Coin { + denom: "uusdc".to_string(), + amount: Uint128::from(1000000000u128), + }, + ], + |result| { + // Ensure we got 999_000 in the response which is 1mil less the initial liquidity amount + assert!(result.unwrap().events.iter().any(|event| { + event.attributes.iter().any(|attr| { + attr.key == "share" + && attr.value + == (Uint128::from(1000000000u128) - MINIMUM_LIQUIDITY_AMOUNT) + .to_string() + }) + })); + }, + ); + + robot.swap( + sender.clone(), + coin(1_000u128, "uusdc"), + "uwhale".to_string(), + None, + None, + None, + "whale-uusdc".to_string(), + vec![Coin { + denom: "uusdc".to_string(), + amount: Uint128::from(1_000u128), + }], + |result| { + result.unwrap(); + }, + ); + + robot + .create_new_epoch() + .query_claimable_epochs_live(Some(sender.clone()), |res| { + let (_, epochs) = res.unwrap(); + assert_eq!(epochs.len(), 1); + }); + + robot.claim(sender, |res| { + println!("{:?}", res); + }); +} diff --git a/contracts/liquidity_hub/bonding-manager/src/tests/rewards.rs b/contracts/liquidity_hub/bonding-manager/src/tests/rewards.rs index ea43f8f8..224d14aa 100644 --- a/contracts/liquidity_hub/bonding-manager/src/tests/rewards.rs +++ b/contracts/liquidity_hub/bonding-manager/src/tests/rewards.rs @@ -1,6 +1,4 @@ -use cosmwasm_std::testing::{mock_dependencies, mock_env}; -use cosmwasm_std::{coin, Coin, Decimal, Uint128, Uint64}; -use white_whale_std::coin; +use cosmwasm_std::{coin, Coin, Decimal, Uint128}; use white_whale_std::fee::{Fee, PoolFee}; use white_whale_std::pool_network::asset::MINIMUM_LIQUIDITY_AMOUNT; @@ -10,15 +8,9 @@ use crate::tests::test_helpers; #[test] fn test_fill_rewards_from_pool_manager() { let mut robot = TestingRobot::default(); - let grace_period = Uint64::new(21); let creator = robot.sender.clone(); let epochs = test_helpers::get_epochs(); - let binding = epochs.clone(); - let claimable_epochs = binding - .iter() - .rev() - .take(grace_period.u64() as usize) - .collect::>(); + let asset_infos = vec!["uwhale".to_string(), "uusdc".to_string()]; // Default Pool fees white_whale_std::pool_network::pair::PoolFee diff --git a/contracts/liquidity_hub/bonding-manager/src/tests/robot.rs b/contracts/liquidity_hub/bonding-manager/src/tests/robot.rs index 5f1e1f2c..4a366855 100644 --- a/contracts/liquidity_hub/bonding-manager/src/tests/robot.rs +++ b/contracts/liquidity_hub/bonding-manager/src/tests/robot.rs @@ -1,9 +1,9 @@ use anyhow::Error; use cosmwasm_std::testing::{mock_dependencies, mock_env, MockApi, MockQuerier, MockStorage}; use cosmwasm_std::{ - coin, from_json, Addr, Coin, Decimal, Empty, OwnedDeps, StdResult, Timestamp, Uint128, Uint64, + coin, from_json, Addr, Coin, Decimal, Empty, OwnedDeps, StdResult, Uint128, Uint64, }; -use cw_multi_test::addons::{MockAddressGenerator, MockApiBech32}; +// use cw_multi_test::addons::{MockAddressGenerator, MockApiBech32}; use cw_multi_test::{ App, AppBuilder, AppResponse, BankKeeper, DistributionKeeper, Executor, FailingModule, GovFailingModule, IbcFailingModule, StakeKeeper, WasmKeeper, @@ -12,7 +12,7 @@ use white_whale_std::fee::PoolFee; use white_whale_testing::multi_test::stargate_mock::StargateMock; use crate::contract::query; -use crate::state::{EPOCHS, LAST_CLAIMED_EPOCH}; +use crate::state::EPOCHS; use cw_multi_test::{Contract, ContractWrapper}; use white_whale_std::bonding_manager::{ BondedResponse, BondingWeightResponse, Config, ExecuteMsg, InstantiateMsg, QueryMsg, @@ -20,8 +20,7 @@ use white_whale_std::bonding_manager::{ }; use white_whale_std::bonding_manager::{ClaimableEpochsResponse, Epoch}; use white_whale_std::epoch_manager::epoch_manager::{Epoch as EpochV2, EpochConfig}; -use white_whale_std::pool_network::asset::{AssetInfo, PairType}; -use white_whale_testing::integration::integration_mocks::mock_app_with_balance; +use white_whale_std::pool_network::asset::PairType; pub fn bonding_manager_contract() -> Box> { let contract = ContractWrapper::new( @@ -74,6 +73,7 @@ pub struct TestingRobot { pub another_sender: Addr, pub bonding_manager_addr: Addr, pub pool_manager_addr: Addr, + pub epoch_manager_addr: Addr, owned_deps: OwnedDeps, env: cosmwasm_std::Env, } @@ -117,6 +117,7 @@ impl TestingRobot { another_sender, bonding_manager_addr: Addr::unchecked(""), pool_manager_addr: Addr::unchecked(""), + epoch_manager_addr: Addr::unchecked(""), owned_deps: mock_dependencies(), env: mock_env(), } @@ -139,10 +140,6 @@ impl TestingRobot { ) } - pub(crate) fn get_bonding_manager_addr(&self) -> Addr { - self.bonding_manager_addr.clone() - } - pub(crate) fn instantiate( &mut self, unbonding_period: Uint64, @@ -155,7 +152,7 @@ impl TestingRobot { "epoch_manager_id: {}", self.app.block_info().time.minus_seconds(10).nanos() ); - let _epoch_manager_addr = self + let epoch_manager_addr = self .app .instantiate_contract( epoch_manager_id, @@ -189,7 +186,7 @@ impl TestingRobot { .app .execute_contract( self.sender.clone(), - _epoch_manager_addr.clone(), + epoch_manager_addr.clone(), &hook_registration_msg, &[], ) @@ -202,7 +199,7 @@ impl TestingRobot { self.app .execute_contract( self.sender.clone(), - _epoch_manager_addr.clone(), + epoch_manager_addr.clone(), &new_epoch_msg, &[], ) @@ -243,6 +240,7 @@ impl TestingRobot { .unwrap(); self.bonding_manager_addr = bonding_manager_addr; self.pool_manager_addr = pool_manager_addr; + self.epoch_manager_addr = epoch_manager_addr; self } @@ -295,6 +293,21 @@ impl TestingRobot { self } + pub(crate) fn claim( + &mut self, + sender: Addr, + response: impl Fn(Result), + ) -> &mut Self { + let msg = ExecuteMsg::Claim {}; + + response( + self.app + .execute_contract(sender, self.bonding_manager_addr.clone(), &msg, &[]), + ); + + self + } + pub(crate) fn withdraw( &mut self, sender: Addr, @@ -311,6 +324,21 @@ impl TestingRobot { self } + pub(crate) fn create_new_epoch(&mut self) -> &mut Self { + let new_epoch_msg = + white_whale_std::epoch_manager::epoch_manager::ExecuteMsg::CreateEpoch {}; + self.app + .execute_contract( + self.sender.clone(), + self.epoch_manager_addr.clone(), + &new_epoch_msg, + &[], + ) + .unwrap(); + + self + } + #[track_caller] pub(crate) fn query_balance( &mut self, @@ -359,17 +387,6 @@ impl TestingRobot { self } - - pub(crate) fn add_last_claimed_epoch_to_state( - &mut self, - address: Addr, - epoch_id: Uint64, - ) -> &mut Self { - LAST_CLAIMED_EPOCH - .save(&mut self.owned_deps.storage, &address, &epoch_id) - .unwrap(); - self - } } fn instantiate_contract( @@ -468,6 +485,37 @@ impl TestingRobot { self } + pub(crate) fn query_claimable_epochs_live( + &mut self, + address: Option, + response: impl Fn(StdResult<(&mut Self, Vec)>), + ) -> &mut Self { + let query_res = if let Some(address) = address { + let bonded_response: ClaimableEpochsResponse = self + .app + .wrap() + .query_wasm_smart( + &self.bonding_manager_addr, + &QueryMsg::Claimable { + addr: address.to_string(), + }, + ) + .unwrap(); + bonded_response + } else { + let bonded_response: ClaimableEpochsResponse = self + .app + .wrap() + .query_wasm_smart(&self.bonding_manager_addr, &QueryMsg::ClaimableEpochs {}) + .unwrap(); + bonded_response + }; + + response(Ok((self, query_res.epochs))); + + self + } + pub(crate) fn query_bonded( &mut self, address: String, diff --git a/contracts/liquidity_hub/bonding-manager/src/tests/withdraw.rs b/contracts/liquidity_hub/bonding-manager/src/tests/withdraw.rs index bf6240d8..fed6f19e 100644 --- a/contracts/liquidity_hub/bonding-manager/src/tests/withdraw.rs +++ b/contracts/liquidity_hub/bonding-manager/src/tests/withdraw.rs @@ -2,7 +2,7 @@ use cosmwasm_std::{coins, Coin, Event, Uint128}; use white_whale_std::bonding_manager::WithdrawableResponse; -use crate::tests::{bond, robot::TestingRobot}; +use crate::tests::robot::TestingRobot; #[test] fn test_withdraw_successfully() { diff --git a/contracts/liquidity_hub/pool-manager/src/manager/commands.rs b/contracts/liquidity_hub/pool-manager/src/manager/commands.rs index 76c988e0..95654699 100644 --- a/contracts/liquidity_hub/pool-manager/src/manager/commands.rs +++ b/contracts/liquidity_hub/pool-manager/src/manager/commands.rs @@ -1,7 +1,9 @@ use cosmwasm_std::{ attr, Attribute, Coin, CosmosMsg, DepsMut, Env, MessageInfo, Response, Uint128, }; -use white_whale_std::{fee::PoolFee, pool_network::asset::PairType, whale_lair::fill_rewards_msg_coin}; +use white_whale_std::{ + fee::PoolFee, pool_network::asset::PairType, whale_lair::fill_rewards_msg_coin, +}; use crate::state::{get_pair_by_identifier, PAIR_COUNTER}; use crate::{