Skip to content

Commit

Permalink
feat: Test bonding and claiming where swaps and pools are created
Browse files Browse the repository at this point in the history
A bit WIP still, few hard codes
  • Loading branch information
0xFable committed Apr 19, 2024
1 parent 8a4e0df commit 780a5e1
Show file tree
Hide file tree
Showing 8 changed files with 327 additions and 43 deletions.
25 changes: 20 additions & 5 deletions contracts/liquidity_hub/bonding-manager/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,15 +227,17 @@ pub fn claim(deps: DepsMut, env: Env, info: MessageInfo) -> Result<Response, Con
!claimable_epochs.is_empty(),
ContractError::NothingToClaim {}

Check warning on line 228 in contracts/liquidity_hub/bonding-manager/src/commands.rs

View check run for this annotation

Codecov / codecov/patch

contracts/liquidity_hub/bonding-manager/src/commands.rs#L228

Added line #L228 was not covered by tests
);

print!("Claimable epochs: {:?}", claimable_epochs);
let global = GLOBAL.load(deps.storage)?;
let mut claimable_fees = vec![];
for mut epoch in claimable_epochs.clone() {
let bonding_weight_response = query_weight(
deps.as_ref(),
env.block.time,
info.sender.to_string(),
Some(epoch.global_index.clone()),
Some(global.clone()),
)?;
println!("Bonding weight response: {:?}", bonding_weight_response);

for fee in epoch.total.iter() {
let reward = fee.amount * bonding_weight_response.share;
Expand Down Expand Up @@ -361,8 +363,15 @@ pub(crate) fn fill_rewards(
skip_swap = true;
}
});
// Suggested method for swaps
// Loop over the assets in info.funds
// If whale is in the fund object then skip that
// Everything else either gets swapped or if its an LP token withdrawn and then swapped
// For each swapped coin we need to simulate swap operations and get the route from SwapRoutes
// For each swapped coin if there is no funds found in the pool found via SwapRoutes, skip it. e.g newly made pools
// Might need to add a reply to the contract as if doing it only in this method we can only save the simulation amount in the state
// Alternatively we could add a reply and try to get the actual amount swapped from there.

println!("Response: {:?}", resp);
if !skip_swap {
let swap_operations = vec![white_whale_std::pool_manager::SwapOperation::WhaleSwap {
token_in_denom: info.funds[0].denom.to_string(),
Expand All @@ -383,15 +392,21 @@ pub(crate) fn fill_rewards(
};
messages.push(wrapped_msg.into());
}

// Note: Might need to convert back to ints and use that for ranking to get the most recent ID
// Note: After swap,
// TODO: Remove hardcode below after more testing
EPOCHS.update(
deps.storage,
&most_recent_epoch_id,
|bucket| -> 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)
},
)?;
Expand Down
13 changes: 13 additions & 0 deletions contracts/liquidity_hub/bonding-manager/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())]));
}
Expand All @@ -155,6 +166,7 @@ pub fn execute(
Some(_) => Err(ContractError::Unauthorized {}),

Check warning on line 166 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#L165-L166

Added lines #L165 - L166 were not covered by tests
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
Expand All @@ -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(),
Expand Down
2 changes: 1 addition & 1 deletion contracts/liquidity_hub/bonding-manager/src/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ pub fn query_claimable(deps: Deps, address: &Addr) -> StdResult<ClaimableEpochsR
claimable_epochs.retain(|epoch| epoch.id > 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.
Expand Down
218 changes: 216 additions & 2 deletions contracts/liquidity_hub/bonding-manager/src/tests/claim.rs
Original file line number Diff line number Diff line change
@@ -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() {
Expand All @@ -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()

Check warning on line 212 in contracts/liquidity_hub/bonding-manager/src/tests/claim.rs

View check run for this annotation

Codecov / codecov/patch

contracts/liquidity_hub/bonding-manager/src/tests/claim.rs#L212

Added line #L212 was not covered by tests
})
}));
},
);

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);
});
}
12 changes: 2 additions & 10 deletions contracts/liquidity_hub/bonding-manager/src/tests/rewards.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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::<Vec<_>>();

let asset_infos = vec!["uwhale".to_string(), "uusdc".to_string()];

// Default Pool fees white_whale_std::pool_network::pair::PoolFee
Expand Down
Loading

0 comments on commit 780a5e1

Please sign in to comment.