Skip to content

Commit

Permalink
chore: add integration tests crate
Browse files Browse the repository at this point in the history
  • Loading branch information
kerber0x authored Sep 2, 2024
2 parents 0611928 + d2461cb commit d1dd1b5
Show file tree
Hide file tree
Showing 38 changed files with 3,600 additions and 309 deletions.
292 changes: 123 additions & 169 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
resolver = "2"

members = [
"integration-tests",
"packages/*",
"contracts/liquidity_hub/pool-network/*",
"contracts/liquidity_hub/fee_collector",
Expand Down Expand Up @@ -73,6 +74,7 @@ terraswap-token = { path = "./contracts/liquidity_hub/pool-network/terraswap_tok
terraswap-pair = { path = "./contracts/liquidity_hub/pool-network/terraswap_pair" }
incentive-manager = { path = "./contracts/liquidity_hub/incentive-manager" }
bonding-manager = { path = "./contracts/liquidity_hub/bonding-manager" }
vault-manager = { path = "./contracts/liquidity_hub/vault-manager" }

[workspace.metadata.dylint]
libraries = [{ git = "https://github.com/0xFable/cw-lint" }]
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ on the Epoch Manager, as the rewards distribution is done based on epochs.
The Epoch Manager is the contract that manages the epochs in the protocol. Its single responsibility is to create the epochs,
which are used by the Incentive and Bonding Managers for distributing incentives and fees.

## Instantiation

Based on the dependencies between the contracts, the instantiation of the contracts is as follows:

- Epoch Manager
- Bonding Manager
- Incentive Manager
- Pool Manager
- Vault Manager

---

## Deployed contracts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,7 @@
]
},
"unbonding_period": {
"description": "Unbonding period in nanoseconds. The time that needs to pass before an unbonded position can be withdrawn",
"description": "Unbonding period in epochs. The time (in epochs) that needs to pass before an unbonded position can be withdrawn",
"type": "integer",
"format": "uint64",
"minimum": 0.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
]
},
"unbonding_period": {
"description": "Unbonding period in nanoseconds. The time that needs to pass before an unbonded position can be withdrawn",
"description": "Unbonding period in epochs. The time (in epochs) that needs to pass before an unbonded position can be withdrawn",
"type": "integer",
"format": "uint64",
"minimum": 0.0
Expand Down
12 changes: 12 additions & 0 deletions contracts/liquidity_hub/bonding-manager/src/bonding/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,18 @@ pub(crate) fn unbond(

if unbond.asset.amount.is_zero() {
BONDS.remove(deps.storage, unbond.id)?;
// check if there are other bonded assets, if not, remove the last claimed epoch to reset the user
let other_bonds_by_receiver = get_bonds_by_receiver(
deps.storage,
info.sender.to_string(),
Some(true),
None,
None,
None,
)?;
if other_bonds_by_receiver.is_empty() {
LAST_CLAIMED_EPOCH.remove(deps.storage, &info.sender);
}
} else {
BONDS.save(deps.storage, unbond.id, &unbond)?;
}
Expand Down
6 changes: 3 additions & 3 deletions contracts/liquidity_hub/bonding-manager/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ 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;
pub const TEMPORAL_BOND_ACTION_REPLY_ID: u64 = 1;

#[entry_point]
pub fn instantiate(
Expand All @@ -38,7 +38,7 @@ pub fn instantiate(

let config = Config {
pool_manager_addr: Addr::unchecked(""),
epoch_manager_addr: Addr::unchecked(""),
epoch_manager_addr: Addr::unchecked(""), // deps.api.addr_validate(&msg.epoch_manager_addr)?,
distribution_denom: msg.distribution_denom,
unbonding_period: msg.unbonding_period,
growth_rate: msg.growth_rate,
Expand Down Expand Up @@ -68,7 +68,7 @@ pub fn instantiate(
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 => {
TEMPORAL_BOND_ACTION_REPLY_ID => {
let TemporalBondAction {
sender,
coin,
Expand Down
56 changes: 35 additions & 21 deletions contracts/liquidity_hub/bonding-manager/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ use white_whale_std::bonding_manager::{
use white_whale_std::constants::LP_SYMBOL;
use white_whale_std::epoch_manager::epoch_manager::EpochResponse;
use white_whale_std::pool_manager::{
PoolInfoResponse, SimulateSwapOperationsResponse, SwapRouteResponse,
PoolsResponse, SimulateSwapOperationsResponse, SwapRouteResponse,
};
use white_whale_std::pool_network::asset;
use white_whale_std::pool_network::asset::aggregate_coins;

use crate::contract::{LP_WITHDRAWAL_REPLY_ID, NEW_EPOCH_CREATION_REPLY_ID};
use crate::contract::{LP_WITHDRAWAL_REPLY_ID, TEMPORAL_BOND_ACTION_REPLY_ID};
use crate::error::ContractError;
use crate::queries::query_claimable;
use crate::state::{
Expand Down Expand Up @@ -128,14 +128,16 @@ pub fn handle_lp_tokens_rewards(
extract_pool_identifier(&lp_token.denom).ok_or(ContractError::AssetMismatch)?;

// make sure a pool with the given identifier exists
let pool: StdResult<PoolInfoResponse> = deps.querier.query_wasm_smart(
let pools_response: StdResult<PoolsResponse> = deps.querier.query_wasm_smart(
config.pool_manager_addr.to_string(),
&white_whale_std::pool_manager::QueryMsg::Pool {
pool_identifier: pool_identifier.to_string(),
&white_whale_std::pool_manager::QueryMsg::Pools {
pool_identifier: Some(pool_identifier.to_string()),
start_after: None,
limit: None,
},
);

if pool.is_err() {
if pools_response.is_err() || pools_response?.pools.is_empty() {
continue;
}

Expand Down Expand Up @@ -194,6 +196,7 @@ pub fn swap_coins_to_main_token(
!coin.denom.contains(".pool.")
& !coin.denom.contains(LP_SYMBOL)
& !coin.denom.eq(distribution_denom)
& !config.bonding_assets.contains(&coin.denom)
})
.collect();
for coin in coins_to_swap {
Expand Down Expand Up @@ -221,25 +224,33 @@ pub fn swap_coins_to_main_token(
// check if the pool has any assets, if not skip the swap
// Note we are only checking the first operation here.
// Might be better to another loop to check all operations
let pool_query = white_whale_std::pool_manager::QueryMsg::Pool {
pool_identifier: swap_routes
.swap_route
.swap_operations
.first()
.unwrap()
.get_pool_identifer(),
let pools_query = white_whale_std::pool_manager::QueryMsg::Pools {
pool_identifier: Some(
swap_routes
.swap_route
.swap_operations
.first()
.unwrap()
.get_pool_identifer(),
),
start_after: None,
limit: None,
};
let mut skip_swap = false;
// Query for the pool to check if it has any assets
let resp: PoolInfoResponse = deps
let pools_response: PoolsResponse = deps
.querier
.query_wasm_smart(config.pool_manager_addr.to_string(), &pool_query)?;
.query_wasm_smart(config.pool_manager_addr.to_string(), &pools_query)?;
// Check pair 'assets' and if either one has 0 amount then don't do swaps
resp.pool_info.assets.iter().for_each(|asset| {
if asset.amount.is_zero() {
skip_swap = true;
}
});
pools_response.pools[0]
.pool_info
.assets
.iter()
.for_each(|asset| {
if asset.amount.is_zero() {
skip_swap = true;
}
});

let simulate_swap_operations_response: SimulateSwapOperationsResponse =
deps.querier.query_wasm_smart(
Expand Down Expand Up @@ -380,6 +391,9 @@ pub fn calculate_rewards(
total_claimable_rewards =
aggregate_coins(&total_claimable_rewards, &vec![reward.clone()])?;

// filter out rewards with zero amount
total_claimable_rewards.retain(|coin| coin.amount > Uint128::zero());

if is_claim {
claimed_rewards_from_bucket =
aggregate_coins(&claimed_rewards_from_bucket, &vec![reward])?;
Expand Down Expand Up @@ -537,6 +551,6 @@ fn create_temporal_bond_action_submsg(
) -> Result<SubMsg, ContractError> {
Ok(SubMsg::reply_on_success(
wasm_execute(contract_addr, msg, vec![])?,
NEW_EPOCH_CREATION_REPLY_ID,
TEMPORAL_BOND_ACTION_REPLY_ID,
))
}
8 changes: 7 additions & 1 deletion contracts/liquidity_hub/bonding-manager/src/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,15 +166,21 @@ pub fn query_claimable(
claimable_reward_buckets.retain(|bucket| !bucket.available.is_empty());
}

// println!(
// ">>> claimable_reward_buckets_2: {:?}",
// claimable_reward_buckets
// );

Ok(ClaimableRewardBucketsResponse {
reward_buckets: claimable_reward_buckets,
})
}

/// Returns the rewards that can be claimed by the given address.
pub(crate) fn query_rewards(deps: Deps, address: String) -> Result<RewardsResponse, ContractError> {
let (rewards, _, _) =
let (mut rewards, _, _) =
helpers::calculate_rewards(&deps, deps.api.addr_validate(&address)?, false)?;
rewards.retain(|coin| coin.amount > Uint128::zero());

Ok(RewardsResponse { rewards })
}
23 changes: 18 additions & 5 deletions contracts/liquidity_hub/bonding-manager/src/rewards/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub(crate) fn on_epoch_created(

// A new epoch has been created, update rewards bucket and forward the expiring bucket
let config = CONFIG.load(deps.storage)?;

ensure!(
info.sender == config.epoch_manager_addr,
ContractError::Unauthorized
Expand All @@ -49,7 +50,13 @@ pub(crate) fn on_epoch_created(

// Create a new reward bucket for the current epoch with the total rewards accrued in the
// upcoming bucket item
let upcoming_bucket = UPCOMING_REWARD_BUCKET.load(deps.storage)?;
let mut upcoming_bucket = UPCOMING_REWARD_BUCKET.load(deps.storage)?;

// Remove all zero amounts from the upcoming bucket
upcoming_bucket
.total
.retain(|coin| coin.amount > Uint128::zero());

let mut new_reward_bucket = RewardBucket {
id: current_epoch.id,
epoch_start_time: current_epoch.start_time,
Expand Down Expand Up @@ -250,11 +257,17 @@ pub fn claim(deps: DepsMut, info: MessageInfo) -> Result<Response, ContractError

LAST_CLAIMED_EPOCH.save(deps.storage, &info.sender, &current_epoch.epoch.id)?;

Ok(Response::default()
// Create the response based on whether there are claimable rewards to avoid sending empty coins
let mut response = Response::default()
.add_attributes(vec![("action", "claim".to_string())])
.add_attributes(attributes)
.add_message(CosmosMsg::Bank(BankMsg::Send {
.add_attributes(attributes);

if !total_claimable_rewards.is_empty() {
response = response.add_message(CosmosMsg::Bank(BankMsg::Send {
to_address: info.sender.to_string(),
amount: total_claimable_rewards,
})))
}));
}

Ok(response)
}
33 changes: 33 additions & 0 deletions contracts/liquidity_hub/bonding-manager/src/tests/claim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1099,4 +1099,37 @@ fn test_rewards_forwarding() {
}
);
});

suite
.swap(
creator.clone(),
coin(80_000u128, "uusdc"),
"uwhale".to_string(),
None,
None,
None,
"whale-uusdc".to_string(),
vec![Coin {
denom: "uusdc".to_string(),
amount: Uint128::from(80_000u128),
}],
|result| {
result.unwrap();
},
)
.add_one_day()
.create_new_epoch()
.claim(creator.clone(), |result| {
result.unwrap();
})
.add_one_day()
.create_new_epoch()
.claim(creator.clone(), |result| {
let err = result.unwrap_err().downcast::<ContractError>().unwrap();

match err {
ContractError::NothingToClaim { .. } => {}
_ => panic!("Wrong error type, should return ContractError::NothingToClaim"),
}
});
}
19 changes: 9 additions & 10 deletions contracts/liquidity_hub/incentive-manager/tests/common/suite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,18 @@ use cw_multi_test::{

use white_whale_std::epoch_manager::epoch_manager::{Epoch, EpochConfig, EpochResponse};
use white_whale_std::epoch_manager::hooks::EpochChangedHookMsg;
use white_whale_std::fee::{Fee, PoolFee};
use white_whale_std::fee::PoolFee;
use white_whale_std::incentive_manager::{
Config, IncentiveAction, IncentivesBy, IncentivesResponse, InstantiateMsg, LpWeightResponse,
PositionAction, PositionsResponse, RewardsResponse,
};
use white_whale_std::pool_manager::PoolType;
use white_whale_std::pool_network::asset::{Asset, AssetInfo};
use white_whale_std::pool_network::asset::AssetInfo;
use white_whale_testing::integration::contracts::whale_lair_contract;
use white_whale_testing::multi_test::stargate_mock::StargateMock;

use crate::common::suite_contracts::{
bonding_manager_contract, epoch_manager_contract, incentive_manager_contract,
pool_manager_contract,
_pool_manager_contract, epoch_manager_contract, incentive_manager_contract,
};

type OsmosisTokenFactoryApp = App<
Expand Down Expand Up @@ -259,8 +258,8 @@ impl TestingSuite {
}

#[allow(clippy::inconsistent_digit_grouping)]
fn create_pool_manager(&mut self) {
let pool_manager_contract = self.app.store_code(pool_manager_contract());
fn _create_pool_manager(&mut self) {
let pool_manager_contract = self.app.store_code(_pool_manager_contract());

// create epoch manager
let msg = white_whale_std::pool_manager::InstantiateMsg {
Expand Down Expand Up @@ -693,7 +692,7 @@ impl TestingSuite {

impl TestingSuite {
#[track_caller]
pub(crate) fn provide_liquidity(
pub(crate) fn _provide_liquidity(
&mut self,
sender: Addr,
pool_identifier: String,
Expand All @@ -718,7 +717,7 @@ impl TestingSuite {
}

#[track_caller]
pub(crate) fn swap(
pub(crate) fn _swap(
&mut self,
sender: Addr,
_offer_asset: Coin,
Expand Down Expand Up @@ -747,7 +746,7 @@ impl TestingSuite {
}

#[track_caller]
pub(crate) fn add_swap_routes(
pub(crate) fn _add_swap_routes(
&mut self,
sender: Addr,
swap_routes: Vec<white_whale_std::pool_manager::SwapRoute>,
Expand All @@ -763,7 +762,7 @@ impl TestingSuite {
self
}
#[track_caller]
pub(crate) fn create_pair(
pub(crate) fn _create_pair(
&mut self,
sender: Addr,
asset_denoms: Vec<String>,
Expand Down
Loading

0 comments on commit d1dd1b5

Please sign in to comment.