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

chore: integration tests crate #364

Merged
merged 20 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
4e3e37b
chore: set foundation for epic integration tests
kerber0x May 23, 2024
343ee5a
chore: add actions for the contracts
kerber0x May 24, 2024
52fbfd1
test(tests-crate): add methods to create pools and vaults
kerber0x May 24, 2024
506557f
chore: replace Pool query for Pools(with pagination) in pool manager
kerber0x May 24, 2024
0f50aff
feat: map some errors and add couple tests to start te flow
nseguias Jun 10, 2024
132e1ba
fix: filter coins with amount 0 on query claimable && query rewards
nseguias Jun 11, 2024
305c67c
test: add default liquidity and swap routes in pool manager
kerber0x Jun 11, 2024
76ca2d0
feat: add prop tests (still WIP)
nseguias Jun 12, 2024
a723074
feat: add check to make sure two identical assets cannot be swapped
nseguias Jun 18, 2024
be083ad
fix: refactor proptest logic to be a bit more real
nseguias Jun 20, 2024
00f346b
feat(pool_manager): pools query improvement (#372)
kerber0x Jul 2, 2024
49dd398
fix: check that receiver_balance > 0 on pool manager's router before …
nseguias Jul 10, 2024
42e8a10
fix: refactor proptests to take into account that bonding and unbondi…
nseguias Jul 10, 2024
7e14219
fix: WIP in integration tests
nseguias Jul 11, 2024
e2c0f8f
fix: WIP in integration tests
nseguias Jul 12, 2024
1511290
fix: on unbond, remove user from when they don't have any more bonde…
nseguias Jul 15, 2024
f8387c5
fix: added two filters to avoid hitting an error on edge cases. Tests…
nseguias Jul 16, 2024
8a74779
feat: add withdraw action to proptests
nseguias Jul 22, 2024
bf28bfb
fix: remove filter that was restricting the amount of epochs to 21
nseguias Aug 16, 2024
d2461cb
Merge branch 'release/v2_contracts' into feat/integration-tests-crate
kerber0x Sep 2, 2024
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
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
Loading