From cb9e553ddbad27bbeeb710ae93ef0c9de6660354 Mon Sep 17 00:00:00 2001 From: Kerber0x Date: Fri, 12 Jan 2024 11:54:57 +0000 Subject: [PATCH] test: fixing tests for the osmosis flag --- Cargo.toml | 3 +- .../fee_collector/src/tests/integration.rs | 3089 +++++++++-------- .../src/tests/mock_instantiate.rs | 82 +- .../src/tests/feature_toggle.rs | 3 + .../src/tests/protocol_fees.rs | 4 + .../src/tests/provide_liquidity.rs | 3 + .../stableswap_3pool/src/tests/queries.rs | 2 + .../stableswap_3pool/src/tests/swap.rs | 5 + .../stableswap_3pool/src/tests/testing.rs | 6 + .../stableswap_3pool/src/tests/withdrawals.rs | 3 + .../terraswap_factory/src/commands.rs | 1 + .../terraswap_factory/src/testing.rs | 448 ++- .../terraswap_pair/src/commands.rs | 6 - .../src/tests/feature_toggle.rs | 3 + .../terraswap_pair/src/tests/protocol_fees.rs | 100 + .../src/tests/provide_liquidity.rs | 3 + .../terraswap_pair/src/tests/queries.rs | 2 + .../terraswap_pair/src/tests/stableswap.rs | 4 + .../terraswap_pair/src/tests/swap.rs | 372 +- .../terraswap_pair/src/tests/testing.rs | 134 +- .../terraswap_pair/src/tests/withdrawals.rs | 4 + .../white-whale/src/pool_network/asset.rs | 2 +- 22 files changed, 2666 insertions(+), 1613 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0281ae77..7661db3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,8 +36,7 @@ thiserror = "1.0.43" osmosis-std-derive = "0.15.3" prost = {version = "0.11.9", default-features = false, features = ["prost-derive"]} prost-types = {version = "0.11.9", default-features = false} -#white-whale = { path = "./packages/white-whale", features = ["osmosis", "token_factory", "osmosis_token_factory", "injective"] } -white-whale = { path = "./packages/white-whale" } +white-whale = { path = "./packages/white-whale", features = ["token_factory", "osmosis_token_factory", "injective"] } white-whale-testing = { path = "./packages/white-whale-testing" } cw-multi-test = { version = "0.16.5" } uint = "0.9.5" diff --git a/contracts/liquidity_hub/fee_collector/src/tests/integration.rs b/contracts/liquidity_hub/fee_collector/src/tests/integration.rs index a953f8f1..6b3007b9 100644 --- a/contracts/liquidity_hub/fee_collector/src/tests/integration.rs +++ b/contracts/liquidity_hub/fee_collector/src/tests/integration.rs @@ -61,16 +61,40 @@ fn collect_all_factories_cw20_fees_successfully() { ) .unwrap(); + #[cfg(feature = "osmosis")] + let osmosis_fee_collector_address = app + .instantiate_contract( + fee_collector_id, + creator.clone().sender, + &InstantiateMsg {}, + &[], + "osmosis_fee_collector", + None, + ) + .unwrap(); + + #[cfg(not(feature = "osmosis"))] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + }; + + #[cfg(feature = "osmosis")] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + osmosis_fee_collector_addr: osmosis_fee_collector_address.to_string(), + }; + let pool_factory_address = app .instantiate_contract( pool_factory_id, creator.clone().sender, - &pool_network::factory::InstantiateMsg { - pair_code_id: pair_id, - trio_code_id: trio_id, - token_code_id: token_id, - fee_collector_addr: fee_collector_address.to_string(), - }, + &instantiate_msg, &[], "fee_collector", None, @@ -135,6 +159,35 @@ fn collect_all_factories_cw20_fees_successfully() { cw20_tokens.push(token_address.clone()); } + #[cfg(not(feature = "osmosis"))] + let pool_fees = PoolFee { + protocol_fee: Fee { + share: Decimal::percent(5u64), + }, + swap_fee: Fee { + share: Decimal::percent(7u64), + }, + burn_fee: Fee { + share: Decimal::zero(), + }, + }; + + #[cfg(feature = "osmosis")] + let pool_fees = PoolFee { + protocol_fee: Fee { + share: Decimal::percent(5u64), + }, + swap_fee: Fee { + share: Decimal::percent(7u64), + }, + burn_fee: Fee { + share: Decimal::zero(), + }, + osmosis_fee: Fee { + share: Decimal::zero(), + }, + }; + // Create few pools let mut pair_tokens: Vec = Vec::new(); for i in 0..TOKEN_AMOUNT - 1 { @@ -151,17 +204,7 @@ fn collect_all_factories_cw20_fees_successfully() { contract_addr: cw20_tokens[i as usize + 1].to_string(), }, ], - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(5u64), - }, - swap_fee: Fee { - share: Decimal::percent(7u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, + pool_fees: pool_fees.clone(), pair_type: PairType::ConstantProduct, token_factory_lp: false, }, @@ -538,6 +581,7 @@ fn collect_all_factories_cw20_fees_successfully() { assert!(balance_res.balance > ask_asset_original_balance); } +#[cfg(not(feature = "osmosis"))] #[test] fn collect_cw20_fees_for_specific_contracts_successfully() { const TOKEN_AMOUNT: usize = 10; @@ -563,16 +607,40 @@ fn collect_cw20_fees_for_specific_contracts_successfully() { ) .unwrap(); + #[cfg(feature = "osmosis")] + let osmosis_fee_collector_address = app + .instantiate_contract( + fee_collector_id, + creator.clone().sender, + &InstantiateMsg {}, + &[], + "osmosis_fee_collector", + None, + ) + .unwrap(); + + #[cfg(not(feature = "osmosis"))] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + }; + + #[cfg(feature = "osmosis")] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + osmosis_fee_collector_addr: osmosis_fee_collector_address.to_string(), + }; + let pool_factory_address = app .instantiate_contract( pool_factory_id, creator.clone().sender, - &pool_network::factory::InstantiateMsg { - pair_code_id: pair_id, - trio_code_id: trio_id, - token_code_id: token_id, - fee_collector_addr: fee_collector_address.to_string(), - }, + &instantiate_msg, &[], "fee_collector", None, @@ -964,7 +1032,7 @@ fn collect_cw20_fees_for_specific_contracts_successfully() { assert_eq!(asset.amount, Uint128::zero()); } } - +#[cfg(not(feature = "osmosis"))] #[test] fn collect_pools_native_fees_successfully() { const TOKEN_AMOUNT: u8 = 3; @@ -996,16 +1064,40 @@ fn collect_pools_native_fees_successfully() { ) .unwrap(); + #[cfg(feature = "osmosis")] + let osmosis_fee_collector_address = app + .instantiate_contract( + fee_collector_id, + creator.clone().sender, + &InstantiateMsg {}, + &[], + "osmosis_fee_collector", + None, + ) + .unwrap(); + + #[cfg(not(feature = "osmosis"))] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + }; + + #[cfg(feature = "osmosis")] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + osmosis_fee_collector_addr: osmosis_fee_collector_address.to_string(), + }; + let pool_factory_address = app .instantiate_contract( pool_factory_id, creator.clone().sender, - &pool_network::factory::InstantiateMsg { - pair_code_id: pair_id, - trio_code_id: trio_id, - token_code_id: token_id, - fee_collector_addr: fee_collector_address.to_string(), - }, + &instantiate_msg, &[], "fee_collector", None, @@ -1558,7 +1650,7 @@ fn collect_pools_native_fees_successfully() { .unwrap(); assert!(balance_res.balance > ask_asset_original_balance); } - +#[cfg(not(feature = "osmosis"))] #[test] fn collect_fees_with_pagination_successfully() { const TOKEN_AMOUNT: usize = 10; @@ -1588,16 +1680,40 @@ fn collect_fees_with_pagination_successfully() { ) .unwrap(); + #[cfg(feature = "osmosis")] + let osmosis_fee_collector_address = app + .instantiate_contract( + fee_collector_id, + creator.clone().sender, + &InstantiateMsg {}, + &[], + "osmosis_fee_collector", + None, + ) + .unwrap(); + + #[cfg(not(feature = "osmosis"))] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + }; + + #[cfg(feature = "osmosis")] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + osmosis_fee_collector_addr: osmosis_fee_collector_address.to_string(), + }; + let pool_factory_address = app .instantiate_contract( pool_factory_id, creator.clone().sender, - &pool_network::factory::InstantiateMsg { - pair_code_id: pair_id, - trio_code_id: trio_id, - token_code_id: token_id, - fee_collector_addr: fee_collector_address.to_string(), - }, + &instantiate_msg, &[], "fee_collector", None, @@ -2207,7 +2323,7 @@ fn collect_fees_for_vault() { }; assert_eq!(fee_collector_fees_query[0], expected_asset); } - +#[cfg(not(feature = "osmosis"))] #[test] fn aggregate_fees_for_vault() { let creator = mock_creator(); @@ -2310,16 +2426,40 @@ fn aggregate_fees_for_vault() { ) .unwrap(); + #[cfg(feature = "osmosis")] + let osmosis_fee_collector_address = app + .instantiate_contract( + fee_collector_id, + creator.clone().sender, + &InstantiateMsg {}, + &[], + "osmosis_fee_collector", + None, + ) + .unwrap(); + + #[cfg(not(feature = "osmosis"))] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + }; + + #[cfg(feature = "osmosis")] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + osmosis_fee_collector_addr: osmosis_fee_collector_address.to_string(), + }; + let pool_factory_address = app .instantiate_contract( pool_factory_id, creator.clone().sender, - &pool_network::factory::InstantiateMsg { - pair_code_id: pair_id, - trio_code_id: trio_id, - token_code_id: token_id, - fee_collector_addr: fee_collector_address.to_string(), - }, + &instantiate_msg, &[], "fee_collector", None, @@ -2844,7 +2984,7 @@ fn accumulate_fee_works() { } } } - +#[cfg(not(feature = "osmosis"))] #[test] fn collect_and_distribute_fees_successfully() { let creator = mock_creator(); @@ -2882,16 +3022,40 @@ fn collect_and_distribute_fees_successfully() { ) .unwrap(); + #[cfg(feature = "osmosis")] + let osmosis_fee_collector_address = app + .instantiate_contract( + fee_collector_id, + creator.clone().sender, + &InstantiateMsg {}, + &[], + "osmosis_fee_collector", + None, + ) + .unwrap(); + + #[cfg(not(feature = "osmosis"))] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + }; + + #[cfg(feature = "osmosis")] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + osmosis_fee_collector_addr: osmosis_fee_collector_address.to_string(), + }; + let pool_factory_address = app .instantiate_contract( pool_factory_id, creator.clone().sender, - &pool_network::factory::InstantiateMsg { - pair_code_id: pair_id, - trio_code_id: trio_id, - token_code_id: token_id, - fee_collector_addr: fee_collector_address.to_string(), - }, + &instantiate_msg, &[], "fee_collector", None, @@ -3226,7 +3390,7 @@ fn collect_and_distribute_fees_successfully() { assert_eq!(fee_distributor_current_epoch_query.epoch.id, Uint64::one()); assert!(!fee_distributor_current_epoch_query.epoch.total.is_empty()); } - +#[cfg(not(feature = "osmosis"))] #[test] fn collect_and_dist_fees_where_one_bonder_is_increasing_weight_no_claims_until_end() { let creator = mock_creator(); @@ -3275,16 +3439,40 @@ fn collect_and_dist_fees_where_one_bonder_is_increasing_weight_no_claims_until_e ) .unwrap(); + #[cfg(feature = "osmosis")] + let osmosis_fee_collector_address = app + .instantiate_contract( + fee_collector_id, + creator.clone().sender, + &InstantiateMsg {}, + &[], + "osmosis_fee_collector", + None, + ) + .unwrap(); + + #[cfg(not(feature = "osmosis"))] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + }; + + #[cfg(feature = "osmosis")] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + osmosis_fee_collector_addr: osmosis_fee_collector_address.to_string(), + }; + let pool_factory_address = app .instantiate_contract( pool_factory_id, creator.clone().sender, - &pool_network::factory::InstantiateMsg { - pair_code_id: pair_id, - trio_code_id: trio_id, - token_code_id: token_id, - fee_collector_addr: fee_collector_address.to_string(), - }, + &instantiate_msg, &[], "fee_collector", None, @@ -4169,7 +4357,7 @@ fn collect_and_dist_fees_where_one_bonder_is_increasing_weight_no_claims_until_e user_1_claims.iter().sum::() + user_2_claims.iter().sum::() ); } - +#[cfg(not(feature = "osmosis"))] #[test] fn collect_and_distribute_fees_with_expiring_epoch_successfully() { let creator = mock_creator(); @@ -4218,16 +4406,40 @@ fn collect_and_distribute_fees_with_expiring_epoch_successfully() { ) .unwrap(); + #[cfg(feature = "osmosis")] + let osmosis_fee_collector_address = app + .instantiate_contract( + fee_collector_id, + creator.clone().sender, + &InstantiateMsg {}, + &[], + "osmosis_fee_collector", + None, + ) + .unwrap(); + + #[cfg(not(feature = "osmosis"))] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + }; + + #[cfg(feature = "osmosis")] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + osmosis_fee_collector_addr: osmosis_fee_collector_address.to_string(), + }; + let pool_factory_address = app .instantiate_contract( pool_factory_id, creator.clone().sender, - &pool_network::factory::InstantiateMsg { - pair_code_id: pair_id, - trio_code_id: trio_id, - token_code_id: token_id, - fee_collector_addr: fee_collector_address.to_string(), - }, + &instantiate_msg, &[], "fee_collector", None, @@ -4730,30 +4942,19 @@ fn collect_and_distribute_fees_with_expiring_epoch_successfully() { <= Uint128::one() ); } - +#[cfg(not(feature = "osmosis"))] #[test] -fn collect_distribute_with_unbonders() { +fn create_epoch_unsuccessfully() { let creator = mock_creator(); - let balances = vec![ - ( - creator.clone().sender, - vec![ - coin(1_000_000_000, "usdc"), - coin(1_000_000_000, "uwhale"), - coin(1_000_000_000, "ampWHALE"), - coin(1_000_000_000, "bWHALE"), - ], - ), - ( - Addr::unchecked("other"), - vec![ - coin(1_000_000_000, "usdc"), - coin(1_000_000_000, "uwhale"), - coin(1_000_000_000, "ampWHALE"), - coin(1_000_000_000, "bWHALE"), - ], - ), - ]; + let balances = vec![( + creator.clone().sender, + vec![ + coin(1_000_000_000, "usdc"), + coin(1_000_000_000, "uwhale"), + coin(1_000_000_000, "ampWHALE"), + coin(1_000_000_000, "bWHALE"), + ], + )]; let mut app = mock_app_with_balance(balances); @@ -4779,16 +4980,40 @@ fn collect_distribute_with_unbonders() { ) .unwrap(); + #[cfg(feature = "osmosis")] + let osmosis_fee_collector_address = app + .instantiate_contract( + fee_collector_id, + creator.clone().sender, + &InstantiateMsg {}, + &[], + "osmosis_fee_collector", + None, + ) + .unwrap(); + + #[cfg(not(feature = "osmosis"))] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + }; + + #[cfg(feature = "osmosis")] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + osmosis_fee_collector_addr: osmosis_fee_collector_address.to_string(), + }; + let pool_factory_address = app .instantiate_contract( pool_factory_id, creator.clone().sender, - &pool_network::factory::InstantiateMsg { - pair_code_id: pair_id, - trio_code_id: trio_id, - token_code_id: token_id, - fee_collector_addr: fee_collector_address.to_string(), - }, + &instantiate_msg, &[], "fee_collector", None, @@ -4829,7 +5054,7 @@ fn collect_distribute_with_unbonders() { whale_lair_id, creator.clone().sender, &white_whale::whale_lair::InstantiateMsg { - unbonding_period: Uint64::new(1u64), + unbonding_period: Uint64::new(1_000_000_000_000u64), growth_rate: Decimal::one(), bonding_assets: vec![ AssetInfo::NativeToken { @@ -4868,20 +5093,6 @@ fn collect_distribute_with_unbonders() { ) .unwrap(); - // add the fee distributor address to the whale lair contract so we can use it as a clock - app.execute_contract( - creator.sender.clone(), - whale_lair_address.clone(), - &white_whale::whale_lair::ExecuteMsg::UpdateConfig { - fee_distributor_addr: Some(fee_distributor_address.to_string()), - owner: None, - unbonding_period: None, - growth_rate: None, - }, - &[], - ) - .unwrap(); - // add pool router address to the fee collector to be able to aggregate fees app.execute_contract( creator.sender.clone(), @@ -5040,46 +5251,8 @@ fn collect_distribute_with_unbonders() { .unwrap(); } - // bond some tokens - app.execute_contract( - creator.sender.clone(), - whale_lair_address.clone(), - &white_whale::whale_lair::ExecuteMsg::Bond { - asset: Asset { - info: AssetInfo::NativeToken { - denom: "ampWHALE".to_string(), - }, - amount: Uint128::new(1_000u128), - }, - }, - &[Coin { - denom: "ampWHALE".to_string(), - amount: Uint128::new(1_000u128), - }], - ) - .unwrap(); - - app.execute_contract( - Addr::unchecked("other").clone(), - whale_lair_address.clone(), - &white_whale::whale_lair::ExecuteMsg::Bond { - asset: Asset { - info: AssetInfo::NativeToken { - denom: "ampWHALE".to_string(), - }, - amount: Uint128::new(1_000u128), - }, - }, - &[Coin { - denom: "ampWHALE".to_string(), - amount: Uint128::new(1_000u128), - }], - ) - .unwrap(); - - // Create some epochs, for the first one all good but for the second we will have an unbonding + // add epochs to the fee distributor. - // Create EPOCH 1 with 100 whale // whale -> native app.execute_contract( creator.sender.clone(), @@ -5089,28 +5262,26 @@ fn collect_distribute_with_unbonders() { info: AssetInfo::NativeToken { denom: "usdc".to_string(), }, - amount: Uint128::new(2_010u128), + amount: Uint128::new(200_000u128), }, belief_price: None, - max_spread: None, + max_spread: Some(Decimal::percent(40u64)), to: None, }, &[Coin { denom: "usdc".to_string(), - amount: Uint128::new(2_010u128), + amount: Uint128::new(200_000u128), }], ) .unwrap(); - // advance the time to one day after the first epoch was created app.set_block(BlockInfo { height: 123456789u64, - time: Timestamp::from_nanos(1678888800_000000000u64), + time: Timestamp::from_nanos(1678802400_000000000u64), chain_id: "".to_string(), }); // Create new epoch, which triggers fee collection, aggregation and distribution - // Verify epoch 1 app.execute_contract( creator.sender.clone(), fee_distributor_address.clone(), @@ -5119,50 +5290,13 @@ fn collect_distribute_with_unbonders() { ) .unwrap(); - // check that a new epoch was created - let expiring_epoch_res: EpochResponse = app - .wrap() - .query_wasm_smart( - fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::CurrentEpoch {}, - ) - .unwrap(); - assert_eq!(expiring_epoch_res.epoch.id, Uint64::one()); - assert_eq!( - expiring_epoch_res.epoch.available, - expiring_epoch_res.epoch.total - ); - assert!(expiring_epoch_res.epoch.claimed.is_empty()); - // Verify expiring_epoch_res.epoch.available, has 100 whale as an Asset - assert_eq!( - expiring_epoch_res.epoch.available, - vec![Asset { - info: AssetInfo::NativeToken { - denom: "uwhale".to_string(), - }, - amount: Uint128::new(100u128), - }] - ); - - // for user in vec![creator.sender.clone(), Addr::unchecked("other")] { - // let weight: BondingWeightResponse = app - // .wrap() - // .query_wasm_smart( - // whale_lair_address.clone(), - // &white_whale::whale_lair::QueryMsg::Weight { - // address: user.to_string(), - // // timestamp: Some(Timestamp::from_nanos(1678888800_000000000u64-1)), - // global_weight: None, - // timestamp: None, - // }, - // ) - // .unwrap(); - // weights.push(weight.weight); - // } - // println!(" -> weights after each bonding 1000 whale: {:?}", weights); + // advance some time, but not enough to create a new epoch + app.set_block(BlockInfo { + height: 123456789u64, + time: Timestamp::from_nanos(1678802500_000000000u64), //less than a day + chain_id: "".to_string(), + }); - // When creating the second epoch, the first one will be expiring since the grace_period was set to 1. - // Make sure the available tokens on the expiring epoch are transferred to the second one. app.execute_contract( creator.sender.clone(), pair_tokens[0].clone(), @@ -5171,319 +5305,151 @@ fn collect_distribute_with_unbonders() { info: AssetInfo::NativeToken { denom: "usdc".to_string(), }, - amount: Uint128::new(2_050u128), + amount: Uint128::new(200_000u128), }, belief_price: None, - max_spread: None, + max_spread: Some(Decimal::percent(40u64)), to: None, }, &[Coin { denom: "usdc".to_string(), - amount: Uint128::new(2_050u128), + amount: Uint128::new(200_000u128), }], ) .unwrap(); - // Get the weight of create.sender before bonding - // Get the weight after bonding ensure its different - let _weight_before: BondingWeightResponse = app - .wrap() - .query_wasm_smart( - whale_lair_address.clone(), - &white_whale::whale_lair::QueryMsg::Weight { - address: creator.sender.to_string(), - // timestamp: Some(Timestamp::from_nanos(1678888800_000000000u64-1)), - global_index: None, - timestamp: None, - }, + let err = app + .execute_contract( + creator.sender.clone(), + fee_distributor_address.clone(), + &NewEpoch {}, + &[], + ) + .unwrap_err(); + + assert_eq!( + err.downcast::().unwrap(), + fee_distributor::ContractError::CurrentEpochNotExpired {} + ); +} + +#[test] +fn aggregate_fees_unsuccessfully() { + let creator = mock_creator(); + + let mut app = mock_app(); + + let fee_collector_id = store_fee_collector_code(&mut app); + let fee_distributor_id = store_fee_distributor_code(&mut app); + + let fee_collector_address = app + .instantiate_contract( + fee_collector_id, + creator.clone().sender, + &InstantiateMsg {}, + &[], + "fee_collector", + None, ) .unwrap(); - // advance the time to one day after the first epoch was created - app.set_block(BlockInfo { - height: 123456789u64, - time: Timestamp::from_nanos(1678888800_000000000u64), - chain_id: "".to_string(), - }); + let fee_distributor_address = app + .instantiate_contract( + fee_distributor_id, + creator.clone().sender, + &white_whale::fee_distributor::InstantiateMsg { + bonding_contract_addr: "whale_lair".to_string(), + fee_collector_addr: fee_collector_address.to_string(), + grace_period: Uint64::new(5), + epoch_config: EpochConfig { + duration: Uint64::new(86400000000000), + genesis_epoch: Default::default(), + }, + distribution_asset: AssetInfo::NativeToken { + denom: "uwhale".to_string(), + }, + }, + &[], + "fee_distributor", + None, + ) + .unwrap(); - // Create new epoch, which triggers fee collection, aggregation and distribution - // Create EPOCH 2 + // update the fee_distributor_address on fee collector app.execute_contract( creator.sender.clone(), - fee_distributor_address.clone(), - &NewEpoch {}, + fee_collector_address.clone(), + &UpdateConfig { + owner: None, + pool_router: None, + fee_distributor: Some(fee_distributor_address.to_string()), + pool_factory: None, + vault_factory: None, + }, &[], ) .unwrap(); - // check that the second epoch was created - let new_epoch_res: EpochResponse = app - .wrap() - .query_wasm_smart( - fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::CurrentEpoch {}, + let err = app + .execute_contract( + Addr::unchecked("anyone"), + fee_collector_address.clone(), + &AggregateFees { + aggregate_fees_for: FeesFor::Contracts { contracts: vec![] }, + }, + &[], ) - .unwrap(); + .unwrap_err(); - assert_eq!(new_epoch_res.epoch.id, Uint64::new(2u64)); - assert_eq!(new_epoch_res.epoch.available, new_epoch_res.epoch.total); - assert!(new_epoch_res.epoch.claimed.is_empty()); + assert_eq!( + err.downcast::().unwrap(), + ContractError::InvalidContractsFeeAggregation {} + ); +} - // check that the available assets for the expired epoch are zero/empty - let expired_epoch_res: EpochResponse = app - .wrap() - .query_wasm_smart( - fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::Epoch { id: Uint64::one() }, - ) - .unwrap(); - assert!(expired_epoch_res.epoch.available.is_empty()); +#[test] +fn forward_fees_unsuccessfully() { + let creator = mock_creator(); - // let weight_after: BondingWeightResponse = app - // .wrap() - // .query_wasm_smart( - // whale_lair_address.clone(), - // &white_whale::whale_lair::QueryMsg::Weight { - // address: creator.sender.to_string(), - // // timestamp: Some(Timestamp::from_nanos(1678888800_000000000u64-1)), - // global_weight: None, - // timestamp: None, - // }, - // ) - // .unwrap(); + let mut app = mock_app(); - // Get weight of other user and ensure its lower than the first - // let user_two_weight: BondingWeightResponse = app - // .wrap() - // .query_wasm_smart( - // whale_lair_address.clone(), - // &white_whale::whale_lair::QueryMsg::Weight { - // address: Addr::unchecked("other").to_string(), - // // timestamp: Some(Timestamp::from_nanos(1678888800_000000000u64-1)), - // global_weight: None, - // timestamp: None, - // }, - // ) - // .unwrap(); + let fee_collector_id = store_fee_collector_code(&mut app); - // since the fees collected for the second epoch were the same for the first, the available - // assets for the second epoch should be twice the amount of the first + let fee_collector_address = app + .instantiate_contract( + fee_collector_id, + creator.clone().sender, + &InstantiateMsg {}, + &[], + "fee_collector", + None, + ) + .unwrap(); - // iterate the new_epoch_res.epoch.available and add up the amounts for each asset - let mut total_amount_new_epoch = Uint128::zero(); - for asset in new_epoch_res.epoch.available { - total_amount_new_epoch += asset.amount; - } - println!("total_amount_new_epoch: {}", total_amount_new_epoch); - let mut total_amount_expired = Uint128::zero(); - //checking against total since total and available where the same, but available is empty now - for asset in expired_epoch_res.epoch.total { - total_amount_expired += asset.amount; - } - println!("total_amount_expired: {}", total_amount_expired); - assert!(total_amount_new_epoch - total_amount_expired > Uint128::zero()); + // try to forward fees from an unauthorized address + let err = app + .execute_contract( + Addr::unchecked("unauthorized"), + fee_collector_address.clone(), + &ForwardFees { + epoch: Default::default(), + forward_fees_as: AssetInfo::NativeToken { + denom: "uwhale".to_string(), + }, + }, + &[], + ) + .unwrap_err(); - let mut user_1_claims: Vec = vec![]; - // let mut user_2_claims: Vec = vec![]; - - // Get the balance of the sender before claiming - let uwhale_balance_before_claiming = app - .wrap() - .query_balance(creator.sender.clone(), "uwhale") - .unwrap() - .amount; - // claim some rewards - app.execute_contract( - creator.sender.clone(), - fee_distributor_address.clone(), - &white_whale::fee_distributor::ExecuteMsg::Claim {}, - &[], - ) - .unwrap(); - - // Get balance after claiming - let uwhale_balance_after_claiming = app - .wrap() - .query_balance(creator.sender.clone(), "uwhale") - .unwrap() - .amount; - // // Verify amount - // assert_eq!( - // uwhale_balance_after_claiming - - // uwhale_balance_before_claiming, Uint128::new(100) - // ); - user_1_claims.push(uwhale_balance_after_claiming - uwhale_balance_before_claiming); - - // Time to unbond with user 1 - app.execute_contract( - creator.sender.clone(), - whale_lair_address.clone(), - &white_whale::whale_lair::ExecuteMsg::Unbond { - asset: Asset { - info: AssetInfo::NativeToken { - denom: "ampWHALE".to_string(), - }, - amount: Uint128::new(500u128), - }, - }, - &[], - ) - .unwrap(); - - // NOTE: Here is where we could check weights if we wanted too - // Make sure the available tokens on the expiring epoch are transferred to the second one. - app.execute_contract( - creator.sender.clone(), - pair_tokens[0].clone(), - &pool_network::pair::ExecuteMsg::Swap { - offer_asset: Asset { - info: AssetInfo::NativeToken { - denom: "usdc".to_string(), - }, - amount: Uint128::new(2_050u128), - }, - belief_price: None, - max_spread: None, - to: None, - }, - &[Coin { - denom: "usdc".to_string(), - amount: Uint128::new(2_050u128), - }], - ) - .unwrap(); - - // Now we can advance time, create a third epoch and check that the fees collected are - // distributed to the users - // advance the time to one day after the second epoch was created - app.set_block(BlockInfo { - height: 123456789u64, - time: Timestamp::from_nanos(3357777600_000000000u64), - chain_id: "".to_string(), - }); - - // Create new epoch, which triggers fee collection, aggregation and distribution - // Create EPOCH 3 - - app.execute_contract( - creator.sender.clone(), - fee_distributor_address.clone(), - &NewEpoch {}, - &[], - ) - .unwrap(); - - // check that the third epoch was created - let new_epoch_res: EpochResponse = app - .wrap() - .query_wasm_smart( - fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::CurrentEpoch {}, - ) - .unwrap(); - - assert_eq!(new_epoch_res.epoch.id, Uint64::new(3u64)); - assert_eq!(new_epoch_res.epoch.available, new_epoch_res.epoch.total); - assert!(new_epoch_res.epoch.claimed.is_empty()); - - // check that the available assets for the expired epoch are zero/empty - let expired_epoch_res: EpochResponse = app - .wrap() - .query_wasm_smart( - fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::Epoch { - id: Uint64::from(2u64), - }, - ) - .unwrap(); - assert!(expired_epoch_res.epoch.available.is_empty()); - - // Advance time one more time - app.set_block(BlockInfo { - height: 123456789u64, - time: Timestamp::from_nanos(503666400_000000000u64), - chain_id: "".to_string(), - }); - - // We should have about triple the amount of fees collected in the third epoch - // compared to the first - // iterate the new_epoch_res.epoch.available and add up the amounts for each asset - let mut total_amount_new_epoch = Uint128::zero(); - - for asset in new_epoch_res.epoch.available { - total_amount_new_epoch += asset.amount; - } - let mut total_amount_expired = Uint128::zero(); - //checking against total since total and available where the same, but available is empty now - for asset in expired_epoch_res.epoch.total { - total_amount_expired += asset.amount; - } - // assert!(total_amount_new_epoch - total_amount_expired > Uint128::zero()); - - // // Query and verify what is due to user 1 - // let claimable: ClaimableEpochsResponse = app.wrap().query_wasm_smart(fee_distributor_address.clone(), &white_whale::fee_distributor::QueryMsg::Claimable { address: creator.sender.to_string() }).unwrap(); - // println!(" -> claimable: {:?}", claimable); - // claim some rewards - let uwhale_balance_before_claiming = app - .wrap() - .query_balance(Addr::unchecked("other"), "uwhale") - .unwrap() - .amount; - // Claim for user 2 - app.execute_contract( - Addr::unchecked("other"), - fee_distributor_address.clone(), - &white_whale::fee_distributor::ExecuteMsg::Claim {}, - &[], - ) - .unwrap(); - - let uwhale_balance_after_claiming = app - .wrap() - .query_balance(Addr::unchecked("other"), "uwhale") - .unwrap() - .amount; - - let user_2_whale_received = uwhale_balance_after_claiming - uwhale_balance_before_claiming; - println!("-> User 2 whale_received: {}", user_2_whale_received); - - // Claim 2 - // claim some rewards - let uwhale_balance_before_claiming = app - .wrap() - .query_balance(creator.sender.clone(), "uwhale") - .unwrap() - .amount; - - app.execute_contract( - creator.sender.clone(), - fee_distributor_address.clone(), - &white_whale::fee_distributor::ExecuteMsg::Claim {}, - &[], - ) - .unwrap(); - - let uwhale_balance_after_claiming = app - .wrap() - .query_balance(creator.sender.clone(), "uwhale") - .unwrap() - .amount; - - let user_1_whale_received = uwhale_balance_after_claiming - uwhale_balance_before_claiming; - println!("whale_received: {}", user_1_whale_received); - - // For these claims, the bonds and weights started the same - // No claims happened - // User 2 should have received more than user 1 as user 1 halfed their bond and thus their weight - // User 1 should not get an even share anymore considering all else stays the same - assert_eq!(user_2_whale_received, Uint128::new(133u128)); - assert_eq!(user_1_whale_received, Uint128::new(66u128)); - - assert_ne!(user_2_whale_received, user_1_whale_received); -} + assert_eq!( + err.downcast::().unwrap(), + ContractError::Unauthorized {} + ); +} +#[cfg(not(feature = "osmosis"))] #[test] -fn create_epoch_unsuccessfully() { +fn decrease_grace_period_fee_distributor() { let creator = mock_creator(); let balances = vec![( creator.clone().sender, @@ -5519,16 +5485,40 @@ fn create_epoch_unsuccessfully() { ) .unwrap(); + #[cfg(feature = "osmosis")] + let osmosis_fee_collector_address = app + .instantiate_contract( + fee_collector_id, + creator.clone().sender, + &InstantiateMsg {}, + &[], + "osmosis_fee_collector", + None, + ) + .unwrap(); + + #[cfg(not(feature = "osmosis"))] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + }; + + #[cfg(feature = "osmosis")] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + osmosis_fee_collector_addr: osmosis_fee_collector_address.to_string(), + }; + let pool_factory_address = app .instantiate_contract( pool_factory_id, creator.clone().sender, - &pool_network::factory::InstantiateMsg { - pair_code_id: pair_id, - trio_code_id: trio_id, - token_code_id: token_id, - fee_collector_addr: fee_collector_address.to_string(), - }, + &instantiate_msg, &[], "fee_collector", None, @@ -5593,7 +5583,7 @@ fn create_epoch_unsuccessfully() { &white_whale::fee_distributor::InstantiateMsg { bonding_contract_addr: whale_lair_address.clone().to_string(), fee_collector_addr: fee_collector_address.clone().to_string(), - grace_period: Uint64::new(1), + grace_period: Uint64::new(2), epoch_config: EpochConfig { duration: Uint64::new(86_400_000_000_000u64), // a day genesis_epoch: Uint64::new(1678802400_000000000u64), // March 14, 2023 2:00:00 PM @@ -5790,9 +5780,10 @@ fn create_epoch_unsuccessfully() { ) .unwrap(); + // advance the time to one day after the first epoch was created app.set_block(BlockInfo { height: 123456789u64, - time: Timestamp::from_nanos(1678802400_000000000u64), + time: Timestamp::from_nanos(1678888800_000000000u64), chain_id: "".to_string(), }); @@ -5805,13 +5796,8 @@ fn create_epoch_unsuccessfully() { ) .unwrap(); - // advance some time, but not enough to create a new epoch - app.set_block(BlockInfo { - height: 123456789u64, - time: Timestamp::from_nanos(1678802500_000000000u64), //less than a day - chain_id: "".to_string(), - }); - + // When creating the second epoch, the first one will be expiring since the grace_period was set to 1/. + // Make sure the available tokens on the expiring epoch are transferred to the second one. app.execute_contract( creator.sender.clone(), pair_tokens[0].clone(), @@ -5833,147 +5819,69 @@ fn create_epoch_unsuccessfully() { ) .unwrap(); + // advance the time to one day after the first epoch was created + app.set_block(BlockInfo { + height: 123456789u64, + time: Timestamp::from_nanos(1678888800_000000000u64), + chain_id: "".to_string(), + }); + + // Create new epoch, which triggers fee collection, aggregation and distribution + app.execute_contract( + creator.sender.clone(), + fee_distributor_address.clone(), + &NewEpoch {}, + &[], + ) + .unwrap(); + + // try updating the grace_period on the config to 1, cannot be decreased let err = app .execute_contract( creator.sender.clone(), fee_distributor_address.clone(), - &NewEpoch {}, + &white_whale::fee_distributor::ExecuteMsg::UpdateConfig { + owner: None, + bonding_contract_addr: None, + fee_collector_addr: None, + grace_period: Some(Uint64::one()), + distribution_asset: None, + epoch_config: None, + }, &[], ) .unwrap_err(); assert_eq!( err.downcast::().unwrap(), - fee_distributor::ContractError::CurrentEpochNotExpired {} + fee_distributor::ContractError::GracePeriodDecrease {} ); } +#[cfg(not(feature = "osmosis"))] #[test] -fn aggregate_fees_unsuccessfully() { +fn users_cannot_claim_rewards_from_past_epochs() { let creator = mock_creator(); - - let mut app = mock_app(); - - let fee_collector_id = store_fee_collector_code(&mut app); - let fee_distributor_id = store_fee_distributor_code(&mut app); - - let fee_collector_address = app - .instantiate_contract( - fee_collector_id, - creator.clone().sender, - &InstantiateMsg {}, - &[], - "fee_collector", - None, - ) - .unwrap(); - - let fee_distributor_address = app - .instantiate_contract( - fee_distributor_id, - creator.clone().sender, - &white_whale::fee_distributor::InstantiateMsg { - bonding_contract_addr: "whale_lair".to_string(), - fee_collector_addr: fee_collector_address.to_string(), - grace_period: Uint64::new(5), - epoch_config: EpochConfig { - duration: Uint64::new(86400000000000), - genesis_epoch: Default::default(), - }, - distribution_asset: AssetInfo::NativeToken { - denom: "uwhale".to_string(), - }, - }, - &[], - "fee_distributor", - None, - ) - .unwrap(); - - // update the fee_distributor_address on fee collector - app.execute_contract( - creator.sender.clone(), - fee_collector_address.clone(), - &UpdateConfig { - owner: None, - pool_router: None, - fee_distributor: Some(fee_distributor_address.to_string()), - pool_factory: None, - vault_factory: None, - }, - &[], - ) - .unwrap(); - - let err = app - .execute_contract( - Addr::unchecked("anyone"), - fee_collector_address.clone(), - &AggregateFees { - aggregate_fees_for: FeesFor::Contracts { contracts: vec![] }, - }, - &[], - ) - .unwrap_err(); - - assert_eq!( - err.downcast::().unwrap(), - ContractError::InvalidContractsFeeAggregation {} - ); -} - -#[test] -fn forward_fees_unsuccessfully() { - let creator = mock_creator(); - - let mut app = mock_app(); - - let fee_collector_id = store_fee_collector_code(&mut app); - - let fee_collector_address = app - .instantiate_contract( - fee_collector_id, + let balances = vec![ + ( creator.clone().sender, - &InstantiateMsg {}, - &[], - "fee_collector", - None, - ) - .unwrap(); - - // try to forward fees from an unauthorized address - let err = app - .execute_contract( - Addr::unchecked("unauthorized"), - fee_collector_address.clone(), - &ForwardFees { - epoch: Default::default(), - forward_fees_as: AssetInfo::NativeToken { - denom: "uwhale".to_string(), - }, - }, - &[], - ) - .unwrap_err(); - - assert_eq!( - err.downcast::().unwrap(), - ContractError::Unauthorized {} - ); -} - -#[test] -fn decrease_grace_period_fee_distributor() { - let creator = mock_creator(); - let balances = vec![( - creator.clone().sender, - vec![ - coin(1_000_000_000, "usdc"), - coin(1_000_000_000, "uwhale"), - coin(1_000_000_000, "ampWHALE"), - coin(1_000_000_000, "bWHALE"), - ], - )]; + vec![ + coin(1_000_000_000, "usdc"), + coin(1_000_000_000, "uwhale"), + coin(1_000_000_000, "ampWHALE"), + coin(1_000_000_000, "bWHALE"), + ], + ), + ( + Addr::unchecked("other"), + vec![ + coin(1_000_000_000, "usdc"), + coin(1_000_000_000, "uwhale"), + coin(1_000_000_000, "ampWHALE"), + coin(1_000_000_000, "bWHALE"), + ], + ), + ]; let mut app = mock_app_with_balance(balances); @@ -5999,16 +5907,40 @@ fn decrease_grace_period_fee_distributor() { ) .unwrap(); + #[cfg(feature = "osmosis")] + let osmosis_fee_collector_address = app + .instantiate_contract( + fee_collector_id, + creator.clone().sender, + &InstantiateMsg {}, + &[], + "osmosis_fee_collector", + None, + ) + .unwrap(); + + #[cfg(not(feature = "osmosis"))] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + }; + + #[cfg(feature = "osmosis")] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + osmosis_fee_collector_addr: osmosis_fee_collector_address.to_string(), + }; + let pool_factory_address = app .instantiate_contract( pool_factory_id, creator.clone().sender, - &pool_network::factory::InstantiateMsg { - pair_code_id: pair_id, - trio_code_id: trio_id, - token_code_id: token_id, - fee_collector_addr: fee_collector_address.to_string(), - }, + &instantiate_msg, &[], "fee_collector", None, @@ -6073,7 +6005,7 @@ fn decrease_grace_period_fee_distributor() { &white_whale::fee_distributor::InstantiateMsg { bonding_contract_addr: whale_lair_address.clone().to_string(), fee_collector_addr: fee_collector_address.clone().to_string(), - grace_period: Uint64::new(2), + grace_period: Uint64::new(3u64), epoch_config: EpochConfig { duration: Uint64::new(86_400_000_000_000u64), // a day genesis_epoch: Uint64::new(1678802400_000000000u64), // March 14, 2023 2:00:00 PM @@ -6088,6 +6020,19 @@ fn decrease_grace_period_fee_distributor() { ) .unwrap(); + // add the fee distributor address to the whale lair contract so we can use it as a clock + app.execute_contract( + creator.sender.clone(), + whale_lair_address.clone(), + &white_whale::whale_lair::ExecuteMsg::UpdateConfig { + fee_distributor_addr: Some(fee_distributor_address.to_string()), + owner: None, + unbonding_period: None, + growth_rate: None, + }, + &[], + ) + .unwrap(); // add pool router address to the fee collector to be able to aggregate fees app.execute_contract( creator.sender.clone(), @@ -6246,6 +6191,25 @@ fn decrease_grace_period_fee_distributor() { .unwrap(); } + // bond some tokens + app.execute_contract( + creator.sender.clone(), + whale_lair_address.clone(), + &white_whale::whale_lair::ExecuteMsg::Bond { + asset: Asset { + info: AssetInfo::NativeToken { + denom: "bWHALE".to_string(), + }, + amount: Uint128::new(300_000_000u128), + }, + }, + &[Coin { + denom: "bWHALE".to_string(), + amount: Uint128::new(300_000_000u128), + }], + ) + .unwrap(); + // add epochs to the fee distributor. // whale -> native @@ -6286,8 +6250,22 @@ fn decrease_grace_period_fee_distributor() { ) .unwrap(); - // When creating the second epoch, the first one will be expiring since the grace_period was set to 1/. - // Make sure the available tokens on the expiring epoch are transferred to the second one. + // check that a new epoch was created + let expiring_epoch_res: EpochResponse = app + .wrap() + .query_wasm_smart( + fee_distributor_address.clone(), + &white_whale::fee_distributor::QueryMsg::CurrentEpoch {}, + ) + .unwrap(); + assert_eq!(expiring_epoch_res.epoch.id, Uint64::one()); + assert_eq!( + expiring_epoch_res.epoch.available, + expiring_epoch_res.epoch.total + ); + assert!(expiring_epoch_res.epoch.claimed.is_empty()); + + // Create second epoch app.execute_contract( creator.sender.clone(), pair_tokens[0].clone(), @@ -6325,51 +6303,151 @@ fn decrease_grace_period_fee_distributor() { ) .unwrap(); - // try updating the grace_period on the config to 1, cannot be decreased - let err = app - .execute_contract( - creator.sender.clone(), + // check that the second epoch was created + let new_epoch_res: EpochResponse = app + .wrap() + .query_wasm_smart( fee_distributor_address.clone(), - &white_whale::fee_distributor::ExecuteMsg::UpdateConfig { - owner: None, - bonding_contract_addr: None, - fee_collector_addr: None, - grace_period: Some(Uint64::one()), - distribution_asset: None, - epoch_config: None, - }, - &[], + &white_whale::fee_distributor::QueryMsg::CurrentEpoch {}, ) - .unwrap_err(); + .unwrap(); - assert_eq!( - err.downcast::().unwrap(), - fee_distributor::ContractError::GracePeriodDecrease {} - ); -} + assert_eq!(new_epoch_res.epoch.id, Uint64::new(2u64)); + assert_eq!(new_epoch_res.epoch.available, new_epoch_res.epoch.total); + assert!(new_epoch_res.epoch.claimed.is_empty()); -#[test] -fn users_cannot_claim_rewards_from_past_epochs() { - let creator = mock_creator(); - let balances = vec![ - ( - creator.clone().sender, - vec![ - coin(1_000_000_000, "usdc"), - coin(1_000_000_000, "uwhale"), - coin(1_000_000_000, "ampWHALE"), - coin(1_000_000_000, "bWHALE"), - ], - ), - ( - Addr::unchecked("other"), - vec![ - coin(1_000_000_000, "usdc"), - coin(1_000_000_000, "uwhale"), - coin(1_000_000_000, "ampWHALE"), - coin(1_000_000_000, "bWHALE"), - ], - ), + // bond with the other account at epoch 2. He shouldn't have anything claimable + app.execute_contract( + Addr::unchecked("other").clone(), + whale_lair_address.clone(), + &white_whale::whale_lair::ExecuteMsg::Bond { + asset: Asset { + info: AssetInfo::NativeToken { + denom: "ampWHALE".to_string(), + }, + amount: Uint128::new(100_000_000u128), + }, + }, + &[Coin { + denom: "ampWHALE".to_string(), + amount: Uint128::new(100_000_000u128), + }], + ) + .unwrap(); + + // make sure the other account cannot claim anything for epoch 1 + let claimable_epochs_res: ClaimableEpochsResponse = app + .wrap() + .query_wasm_smart( + fee_distributor_address.clone(), + &white_whale::fee_distributor::QueryMsg::Claimable { + address: "other".to_string(), + }, + ) + .unwrap(); + assert_eq!(claimable_epochs_res.epochs.len(), 0); + + // Create second epoch + app.execute_contract( + creator.sender.clone(), + pair_tokens[0].clone(), + &pool_network::pair::ExecuteMsg::Swap { + offer_asset: Asset { + info: AssetInfo::NativeToken { + denom: "usdc".to_string(), + }, + amount: Uint128::new(200_000u128), + }, + belief_price: None, + max_spread: Some(Decimal::percent(40u64)), + to: None, + }, + &[Coin { + denom: "usdc".to_string(), + amount: Uint128::new(200_000u128), + }], + ) + .unwrap(); + + // advance the time to one day after the first epoch was created + app.set_block(BlockInfo { + height: 123456789u64, + time: Timestamp::from_nanos(1678978800_000000000u64), + chain_id: "".to_string(), + }); + + // Create new epoch + app.execute_contract( + creator.sender.clone(), + fee_distributor_address.clone(), + &NewEpoch {}, + &[], + ) + .unwrap(); + + // check that the second epoch was created + let new_epoch_res: EpochResponse = app + .wrap() + .query_wasm_smart( + fee_distributor_address.clone(), + &white_whale::fee_distributor::QueryMsg::CurrentEpoch {}, + ) + .unwrap(); + + assert_eq!(new_epoch_res.epoch.id, Uint64::new(3u64)); + assert_eq!(new_epoch_res.epoch.available, new_epoch_res.epoch.total); + assert!(new_epoch_res.epoch.claimed.is_empty()); + + let claimable_epochs_res: ClaimableEpochsResponse = app + .wrap() + .query_wasm_smart( + fee_distributor_address.clone(), + &white_whale::fee_distributor::QueryMsg::Claimable { + address: "other".to_string(), + }, + ) + .unwrap(); + + assert_eq!(claimable_epochs_res.epochs.len(), 1); + // only epoch 3 should be claimable to the other account + assert_eq!(claimable_epochs_res.epochs[0].id, Uint64::new(3u64)); + + let claimable_epochs_res: ClaimableEpochsResponse = app + .wrap() + .query_wasm_smart( + fee_distributor_address.clone(), + &white_whale::fee_distributor::QueryMsg::Claimable { + address: creator.sender.clone().to_string(), + }, + ) + .unwrap(); + // creator bonded before the genesis epoch was created, it means he has right to claim from epoch 1 + assert_eq!(claimable_epochs_res.epochs.len(), 3); +} + +#[cfg(not(feature = "osmosis"))] +#[test] +fn user_can_claim_even_when_his_weight_increases_for_past_epochs() { + let creator = mock_creator(); + let balances = vec![ + ( + creator.clone().sender, + vec![ + coin(1_000_000_000, "usdc"), + coin(1_000_000_000, "uwhale"), + coin(1_000_000_000, "ampWHALE"), + coin(1_000_000_000, "bWHALE"), + ], + ), + ( + Addr::unchecked("other"), + vec![ + coin(1_000_000_000, "usdc"), + coin(1_000_000_000, "uwhale"), + coin(1_000_000_000, "ampWHALE"), + coin(1_000_000_000, "bWHALE"), + ], + ), ]; let mut app = mock_app_with_balance(balances); @@ -6396,16 +6474,40 @@ fn users_cannot_claim_rewards_from_past_epochs() { ) .unwrap(); + #[cfg(feature = "osmosis")] + let osmosis_fee_collector_address = app + .instantiate_contract( + fee_collector_id, + creator.clone().sender, + &InstantiateMsg {}, + &[], + "osmosis_fee_collector", + None, + ) + .unwrap(); + + #[cfg(not(feature = "osmosis"))] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + }; + + #[cfg(feature = "osmosis")] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + osmosis_fee_collector_addr: osmosis_fee_collector_address.to_string(), + }; + let pool_factory_address = app .instantiate_contract( pool_factory_id, creator.clone().sender, - &pool_network::factory::InstantiateMsg { - pair_code_id: pair_id, - trio_code_id: trio_id, - token_code_id: token_id, - fee_collector_addr: fee_collector_address.to_string(), - }, + &instantiate_msg, &[], "fee_collector", None, @@ -6656,7 +6758,7 @@ fn users_cannot_claim_rewards_from_past_epochs() { .unwrap(); } - // bond some tokens + // bond some tokens with both users app.execute_contract( creator.sender.clone(), whale_lair_address.clone(), @@ -6675,6 +6777,24 @@ fn users_cannot_claim_rewards_from_past_epochs() { ) .unwrap(); + app.execute_contract( + Addr::unchecked("other").clone(), + whale_lair_address.clone(), + &white_whale::whale_lair::ExecuteMsg::Bond { + asset: Asset { + info: AssetInfo::NativeToken { + denom: "ampWHALE".to_string(), + }, + amount: Uint128::new(100_000_000u128), + }, + }, + &[Coin { + denom: "ampWHALE".to_string(), + amount: Uint128::new(100_000_000u128), + }], + ) + .unwrap(); + // add epochs to the fee distributor. // whale -> native @@ -6781,26 +6901,19 @@ fn users_cannot_claim_rewards_from_past_epochs() { assert_eq!(new_epoch_res.epoch.available, new_epoch_res.epoch.total); assert!(new_epoch_res.epoch.claimed.is_empty()); - // bond with the other account at epoch 2. He shouldn't have anything claimable - app.execute_contract( - Addr::unchecked("other").clone(), - whale_lair_address.clone(), - &white_whale::whale_lair::ExecuteMsg::Bond { - asset: Asset { - info: AssetInfo::NativeToken { - denom: "ampWHALE".to_string(), - }, - amount: Uint128::new(100_000_000u128), + // check what's claimable for both users + let claimable_epochs_res: ClaimableEpochsResponse = app + .wrap() + .query_wasm_smart( + fee_distributor_address.clone(), + &white_whale::fee_distributor::QueryMsg::Claimable { + address: "creator".to_string(), }, - }, - &[Coin { - denom: "ampWHALE".to_string(), - amount: Uint128::new(100_000_000u128), - }], - ) - .unwrap(); + ) + .unwrap(); + // should be able to claim 2 epochs + assert_eq!(claimable_epochs_res.epochs.len(), 2); - // make sure the other account cannot claim anything for epoch 1 let claimable_epochs_res: ClaimableEpochsResponse = app .wrap() .query_wasm_smart( @@ -6810,88 +6923,170 @@ fn users_cannot_claim_rewards_from_past_epochs() { }, ) .unwrap(); - assert_eq!(claimable_epochs_res.epochs.len(), 0); - - // Create second epoch - app.execute_contract( - creator.sender.clone(), - pair_tokens[0].clone(), - &pool_network::pair::ExecuteMsg::Swap { - offer_asset: Asset { - info: AssetInfo::NativeToken { - denom: "usdc".to_string(), - }, - amount: Uint128::new(200_000u128), - }, - belief_price: None, - max_spread: Some(Decimal::percent(40u64)), - to: None, - }, - &[Coin { - denom: "usdc".to_string(), - amount: Uint128::new(200_000u128), - }], - ) - .unwrap(); - - // advance the time to one day after the first epoch was created - app.set_block(BlockInfo { - height: 123456789u64, - time: Timestamp::from_nanos(1678978800_000000000u64), - chain_id: "".to_string(), - }); + // should be able to claim 2 epochs + assert_eq!(claimable_epochs_res.epochs.len(), 2); - // Create new epoch + // claim with creator app.execute_contract( creator.sender.clone(), fee_distributor_address.clone(), - &NewEpoch {}, + &white_whale::fee_distributor::ExecuteMsg::Claim {}, &[], ) .unwrap(); - // check that the second epoch was created - let new_epoch_res: EpochResponse = app + let claimable_epochs_res: ClaimableEpochsResponse = app .wrap() .query_wasm_smart( fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::CurrentEpoch {}, + &white_whale::fee_distributor::QueryMsg::Claimable { + address: "creator".to_string(), + }, ) .unwrap(); + // shouldn't be able to claim any more epochs + assert!(claimable_epochs_res.epochs.is_empty()); - assert_eq!(new_epoch_res.epoch.id, Uint64::new(3u64)); - assert_eq!(new_epoch_res.epoch.available, new_epoch_res.epoch.total); - assert!(new_epoch_res.epoch.claimed.is_empty()); - - let claimable_epochs_res: ClaimableEpochsResponse = app + //query the epochs to check that something has been claimed + let epoch_res: EpochResponse = app .wrap() .query_wasm_smart( fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::Claimable { - address: "other".to_string(), + &white_whale::fee_distributor::QueryMsg::Epoch { + id: Uint64::new(2u64), }, ) .unwrap(); + assert_eq!(epoch_res.epoch.id, Uint64::new(2u64)); + assert_eq!(epoch_res.epoch.claimed.len(), 1usize); - assert_eq!(claimable_epochs_res.epochs.len(), 1); - // only epoch 3 should be claimable to the other account - assert_eq!(claimable_epochs_res.epochs[0].id, Uint64::new(3u64)); + let epoch_res: EpochResponse = app + .wrap() + .query_wasm_smart( + fee_distributor_address.clone(), + &white_whale::fee_distributor::QueryMsg::Epoch { + id: Uint64::new(1u64), + }, + ) + .unwrap(); + assert_eq!(epoch_res.epoch.id, Uint64::new(1u64)); + assert_eq!(epoch_res.epoch.claimed.len(), 1usize); - let claimable_epochs_res: ClaimableEpochsResponse = app + let _claimable_epochs_res: ClaimableEpochsResponse = app .wrap() .query_wasm_smart( fee_distributor_address.clone(), &white_whale::fee_distributor::QueryMsg::Claimable { - address: creator.sender.clone().to_string(), + address: "other".to_string(), }, ) .unwrap(); - // creator bonded before the genesis epoch was created, it means he has right to claim from epoch 1 - assert_eq!(claimable_epochs_res.epochs.len(), 3); + + // let's unbond everything with creator" + app.execute_contract( + creator.sender.clone(), + whale_lair_address.clone(), + &white_whale::whale_lair::ExecuteMsg::Unbond { + asset: Asset { + info: AssetInfo::NativeToken { + denom: "bWHALE".to_string(), + }, + amount: Uint128::new(300_000_000u128), + }, + }, + &[], + ) + .unwrap(); + + // advance the time + app.execute_contract( + creator.sender.clone(), + pair_tokens[0].clone(), + &pool_network::pair::ExecuteMsg::Swap { + offer_asset: Asset { + info: AssetInfo::NativeToken { + denom: "usdc".to_string(), + }, + amount: Uint128::new(200_000u128), + }, + belief_price: None, + max_spread: Some(Decimal::percent(40u64)), + to: None, + }, + &[Coin { + denom: "usdc".to_string(), + amount: Uint128::new(200_000u128), + }], + ) + .unwrap(); + + app.set_block(BlockInfo { + height: 123456789u64, + time: Timestamp::from_nanos(1678978800_000000000u64), + chain_id: "".to_string(), + }); + + // Create new epoch, which triggers fee collection, aggregation and distribution + app.execute_contract( + creator.sender.clone(), + fee_distributor_address.clone(), + &NewEpoch {}, + &[], + ) + .unwrap(); + + // check that the second epoch was created + let new_epoch_res: EpochResponse = app + .wrap() + .query_wasm_smart( + fee_distributor_address.clone(), + &white_whale::fee_distributor::QueryMsg::CurrentEpoch {}, + ) + .unwrap(); + + assert_eq!(new_epoch_res.epoch.id, Uint64::new(3u64)); + assert_eq!(new_epoch_res.epoch.available, new_epoch_res.epoch.total); + assert!(new_epoch_res.epoch.claimed.is_empty()); + + let _claimable_epochs_res: ClaimableEpochsResponse = app + .wrap() + .query_wasm_smart( + fee_distributor_address.clone(), + &white_whale::fee_distributor::QueryMsg::Claimable { + address: "other".to_string(), + }, + ) + .unwrap(); + // try to claim now with other + app.execute_contract( + Addr::unchecked("other"), + fee_distributor_address.clone(), + &white_whale::fee_distributor::ExecuteMsg::Claim {}, + &[], + ) + .unwrap(); + + let _claimable_epochs_res: ClaimableEpochsResponse = app + .wrap() + .query_wasm_smart( + fee_distributor_address.clone(), + &white_whale::fee_distributor::QueryMsg::Claimable { + address: "creator".to_string(), + }, + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("creator"), + fee_distributor_address.clone(), + &white_whale::fee_distributor::ExecuteMsg::Claim {}, + &[], + ) + .unwrap(); } +#[cfg(not(feature = "osmosis"))] #[test] -fn user_can_claim_even_when_his_weight_increases_for_past_epochs() { +fn user_weight_accounts_for_unbondings() { let creator = mock_creator(); let balances = vec![ ( @@ -6938,16 +7133,40 @@ fn user_can_claim_even_when_his_weight_increases_for_past_epochs() { ) .unwrap(); + #[cfg(feature = "osmosis")] + let osmosis_fee_collector_address = app + .instantiate_contract( + fee_collector_id, + creator.clone().sender, + &InstantiateMsg {}, + &[], + "osmosis_fee_collector", + None, + ) + .unwrap(); + + #[cfg(not(feature = "osmosis"))] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + }; + + #[cfg(feature = "osmosis")] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + osmosis_fee_collector_addr: osmosis_fee_collector_address.to_string(), + }; + let pool_factory_address = app .instantiate_contract( pool_factory_id, creator.clone().sender, - &pool_network::factory::InstantiateMsg { - pair_code_id: pair_id, - trio_code_id: trio_id, - token_code_id: token_id, - fee_collector_addr: fee_collector_address.to_string(), - }, + &instantiate_msg, &[], "fee_collector", None, @@ -7198,6 +7417,8 @@ fn user_can_claim_even_when_his_weight_increases_for_past_epochs() { .unwrap(); } + // creator has 75% while other 25% + // bond some tokens with both users app.execute_contract( creator.sender.clone(), @@ -7207,12 +7428,30 @@ fn user_can_claim_even_when_his_weight_increases_for_past_epochs() { info: AssetInfo::NativeToken { denom: "bWHALE".to_string(), }, - amount: Uint128::new(300_000_000u128), + amount: Uint128::new(100_000_000u128), }, }, &[Coin { denom: "bWHALE".to_string(), - amount: Uint128::new(300_000_000u128), + amount: Uint128::new(100_000_000u128), + }], + ) + .unwrap(); + + app.execute_contract( + creator.sender.clone(), + whale_lair_address.clone(), + &white_whale::whale_lair::ExecuteMsg::Bond { + asset: Asset { + info: AssetInfo::NativeToken { + denom: "ampWHALE".to_string(), + }, + amount: Uint128::new(200_000_000u128), + }, + }, + &[Coin { + denom: "ampWHALE".to_string(), + amount: Uint128::new(200_000_000u128), }], ) .unwrap(); @@ -7422,23 +7661,23 @@ fn user_can_claim_even_when_his_weight_increases_for_past_epochs() { ) .unwrap(); - // let's unbond everything with creator" + // let's unbond just ampWHALE with creator, so it has bWHALE left. That would leave creator and other with 50% of the rewards each app.execute_contract( creator.sender.clone(), whale_lair_address.clone(), &white_whale::whale_lair::ExecuteMsg::Unbond { asset: Asset { info: AssetInfo::NativeToken { - denom: "bWHALE".to_string(), + denom: "ampWHALE".to_string(), }, - amount: Uint128::new(300_000_000u128), + amount: Uint128::new(200_000_000u128), }, }, &[], ) .unwrap(); - // advance the time + // prepare to create a new epoch app.execute_contract( creator.sender.clone(), pair_tokens[0].clone(), @@ -7460,6 +7699,7 @@ fn user_can_claim_even_when_his_weight_increases_for_past_epochs() { ) .unwrap(); + // advance the time app.set_block(BlockInfo { height: 123456789u64, time: Timestamp::from_nanos(1678978800_000000000u64), @@ -7483,12 +7723,11 @@ fn user_can_claim_even_when_his_weight_increases_for_past_epochs() { &white_whale::fee_distributor::QueryMsg::CurrentEpoch {}, ) .unwrap(); - assert_eq!(new_epoch_res.epoch.id, Uint64::new(3u64)); assert_eq!(new_epoch_res.epoch.available, new_epoch_res.epoch.total); assert!(new_epoch_res.epoch.claimed.is_empty()); - let _claimable_epochs_res: ClaimableEpochsResponse = app + let claimable_epochs_res: ClaimableEpochsResponse = app .wrap() .query_wasm_smart( fee_distributor_address.clone(), @@ -7497,62 +7736,241 @@ fn user_can_claim_even_when_his_weight_increases_for_past_epochs() { }, ) .unwrap(); - // try to claim now with other + assert_eq!(claimable_epochs_res.epochs.len(), 3usize); + + let claimable_epochs_res: ClaimableEpochsResponse = app + .wrap() + .query_wasm_smart( + fee_distributor_address.clone(), + &white_whale::fee_distributor::QueryMsg::Claimable { + address: "creator".to_string(), + }, + ) + .unwrap(); + assert_eq!(claimable_epochs_res.epochs.len(), 1usize); + app.execute_contract( - Addr::unchecked("other"), + Addr::unchecked("creator"), fee_distributor_address.clone(), &white_whale::fee_distributor::ExecuteMsg::Claim {}, &[], ) .unwrap(); - let _claimable_epochs_res: ClaimableEpochsResponse = app + let epoch_res: EpochResponse = app .wrap() .query_wasm_smart( fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::Claimable { - address: "creator".to_string(), + &white_whale::fee_distributor::QueryMsg::Epoch { + id: Uint64::new(3u64), }, ) .unwrap(); + assert_eq!(epoch_res.epoch.id, Uint64::new(3u64)); + // creator claimed 50% of the rewards + assert_eq!( + epoch_res.epoch.available, + vec![Asset { + info: NativeToken { + denom: "uwhale".to_string() + }, + amount: Uint128::new(1324u128) + }] + ); + assert_eq!( + epoch_res.epoch.claimed, + vec![Asset { + info: NativeToken { + denom: "uwhale".to_string() + }, + amount: Uint128::new(1323u128) + }] + ); + + //let's claim the rewards for other app.execute_contract( - Addr::unchecked("creator"), + Addr::unchecked("other"), fee_distributor_address.clone(), &white_whale::fee_distributor::ExecuteMsg::Claim {}, &[], ) .unwrap(); -} - -#[test] -fn user_weight_accounts_for_unbondings() { - let creator = mock_creator(); - let balances = vec![ - ( - creator.clone().sender, - vec![ - coin(1_000_000_000, "usdc"), - coin(1_000_000_000, "uwhale"), - coin(1_000_000_000, "ampWHALE"), - coin(1_000_000_000, "bWHALE"), - ], - ), - ( - Addr::unchecked("other"), - vec![ - coin(1_000_000_000, "usdc"), - coin(1_000_000_000, "uwhale"), - coin(1_000_000_000, "ampWHALE"), - coin(1_000_000_000, "bWHALE"), - ], - ), - ]; - let mut app = mock_app_with_balance(balances); + let epoch_res: EpochResponse = app + .wrap() + .query_wasm_smart( + fee_distributor_address.clone(), + &white_whale::fee_distributor::QueryMsg::Epoch { + id: Uint64::new(3u64), + }, + ) + .unwrap(); + assert_eq!(epoch_res.epoch.id, Uint64::new(3u64)); - let fee_collector_id = store_fee_collector_code(&mut app); - let fee_distributor_id = store_fee_distributor_code(&mut app); - let whale_lair_id = store_whale_lair_code(&mut app); + assert_eq!( + epoch_res.epoch.available, + vec![Asset { + info: NativeToken { + denom: "uwhale".to_string() + }, + amount: Uint128::one() + }] + ); + assert_eq!( + epoch_res.epoch.claimed, + vec![Asset { + info: NativeToken { + denom: "uwhale".to_string() + }, + amount: Uint128::new(2646u128) + }] + ); + assert!( + (epoch_res.epoch.claimed.first().unwrap().amount.u128() as i128 + - epoch_res.epoch.total.first().unwrap().amount.u128() as i128) + .abs() + < 2i128 + ); + + //now unbond partially, check if weight is computed correctly + app.execute_contract( + creator.sender.clone(), + whale_lair_address.clone(), + &white_whale::whale_lair::ExecuteMsg::Unbond { + asset: Asset { + info: AssetInfo::NativeToken { + denom: "bWHALE".to_string(), + }, + amount: Uint128::new(50_000_000u128), + }, + }, + &[], + ) + .unwrap(); + + // cretor -> 50bwhale -> 33% + // other -> 100bwhale -> 66% + + // prepare to create a new epoch + app.execute_contract( + creator.sender.clone(), + pair_tokens[0].clone(), + &pool_network::pair::ExecuteMsg::Swap { + offer_asset: Asset { + info: AssetInfo::NativeToken { + denom: "usdc".to_string(), + }, + amount: Uint128::new(200_000u128), + }, + belief_price: None, + max_spread: Some(Decimal::percent(40u64)), + to: None, + }, + &[Coin { + denom: "usdc".to_string(), + amount: Uint128::new(200_000u128), + }], + ) + .unwrap(); + + // advance the time + app.set_block(BlockInfo { + height: 123456789u64, + time: Timestamp::from_nanos(1679065200_000000000u64), + chain_id: "".to_string(), + }); + + // Create new epoch, which triggers fee collection, aggregation and distribution + app.execute_contract( + creator.sender.clone(), + fee_distributor_address.clone(), + &NewEpoch {}, + &[], + ) + .unwrap(); + + // check that the second epoch was created + let new_epoch_res: EpochResponse = app + .wrap() + .query_wasm_smart( + fee_distributor_address.clone(), + &white_whale::fee_distributor::QueryMsg::CurrentEpoch {}, + ) + .unwrap(); + assert_eq!(new_epoch_res.epoch.id, Uint64::new(4u64)); + assert_eq!(new_epoch_res.epoch.available, new_epoch_res.epoch.total); + assert!(new_epoch_res.epoch.claimed.is_empty()); + + // now the creator has 33% of the total weight, and other has 66% + app.execute_contract( + Addr::unchecked("creator"), + fee_distributor_address.clone(), + &white_whale::fee_distributor::ExecuteMsg::Claim {}, + &[], + ) + .unwrap(); + + let epoch_res: EpochResponse = app + .wrap() + .query_wasm_smart( + fee_distributor_address.clone(), + &white_whale::fee_distributor::QueryMsg::Epoch { + id: Uint64::new(4u64), + }, + ) + .unwrap(); + assert_eq!(epoch_res.epoch.id, Uint64::new(4u64)); + // creator claimed 33 % of the rewards + assert_eq!( + epoch_res.epoch.available, + vec![Asset { + info: NativeToken { + denom: "uwhale".to_string() + }, + amount: Uint128::new(1242u128) + }] + ); + assert_eq!( + epoch_res.epoch.claimed, + vec![Asset { + info: NativeToken { + denom: "uwhale".to_string() + }, + amount: Uint128::new(620u128) + }] + ); +} + +#[cfg(not(feature = "osmosis"))] +#[test] +fn users_can_claim_even_when_global_index_was_taken_after_epoch_was_created() { + let creator = mock_creator(); + let balances = vec![ + ( + creator.clone().sender, + vec![ + coin(1_000_000_000, "usdc"), + coin(1_000_000_000, "uwhale"), + coin(1_000_000_000, "ampWHALE"), + coin(1_000_000_000, "bWHALE"), + ], + ), + ( + Addr::unchecked("other"), + vec![ + coin(1_000_000_000, "usdc"), + coin(1_000_000_000, "uwhale"), + coin(1_000_000_000, "ampWHALE"), + coin(1_000_000_000, "bWHALE"), + ], + ), + ]; + + let mut app = mock_app_with_balance(balances); + + let fee_collector_id = store_fee_collector_code(&mut app); + let fee_distributor_id = store_fee_distributor_code(&mut app); + let whale_lair_id = store_whale_lair_code(&mut app); let pool_factory_id = store_pool_factory_code(&mut app); let pool_router_id = store_pool_router_code(&mut app); let pair_id = store_pair_code(&mut app); @@ -7572,22 +7990,45 @@ fn user_weight_accounts_for_unbondings() { ) .unwrap(); + #[cfg(feature = "osmosis")] + let osmosis_fee_collector_address = app + .instantiate_contract( + fee_collector_id, + creator.clone().sender, + &InstantiateMsg {}, + &[], + "osmosis_fee_collector", + None, + ) + .unwrap(); + + #[cfg(not(feature = "osmosis"))] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + }; + + #[cfg(feature = "osmosis")] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + osmosis_fee_collector_addr: osmosis_fee_collector_address.to_string(), + }; + let pool_factory_address = app .instantiate_contract( pool_factory_id, creator.clone().sender, - &pool_network::factory::InstantiateMsg { - pair_code_id: pair_id, - trio_code_id: trio_id, - token_code_id: token_id, - fee_collector_addr: fee_collector_address.to_string(), - }, + &instantiate_msg, &[], "fee_collector", None, ) .unwrap(); - let pool_router_address = app .instantiate_contract( pool_router_id, @@ -7814,285 +8255,53 @@ fn user_weight_accounts_for_unbondings() { }, amount: Uint128::new(500_000u128), }, - ], - slippage_tolerance: None, - receiver: None, - }, - &[ - Coin { - denom: "uwhale".to_string(), - amount: Uint128::new(500_000u128), - }, - Coin { - denom: native_token.clone().to_string(), - amount: Uint128::new(500_000u128), - }, - ], - ) - .unwrap(); - } - - // creator has 75% while other 25% - - // bond some tokens with both users - app.execute_contract( - creator.sender.clone(), - whale_lair_address.clone(), - &white_whale::whale_lair::ExecuteMsg::Bond { - asset: Asset { - info: AssetInfo::NativeToken { - denom: "bWHALE".to_string(), - }, - amount: Uint128::new(100_000_000u128), - }, - }, - &[Coin { - denom: "bWHALE".to_string(), - amount: Uint128::new(100_000_000u128), - }], - ) - .unwrap(); - - app.execute_contract( - creator.sender.clone(), - whale_lair_address.clone(), - &white_whale::whale_lair::ExecuteMsg::Bond { - asset: Asset { - info: AssetInfo::NativeToken { - denom: "ampWHALE".to_string(), - }, - amount: Uint128::new(200_000_000u128), - }, - }, - &[Coin { - denom: "ampWHALE".to_string(), - amount: Uint128::new(200_000_000u128), - }], - ) - .unwrap(); - - app.execute_contract( - Addr::unchecked("other").clone(), - whale_lair_address.clone(), - &white_whale::whale_lair::ExecuteMsg::Bond { - asset: Asset { - info: AssetInfo::NativeToken { - denom: "ampWHALE".to_string(), - }, - amount: Uint128::new(100_000_000u128), - }, - }, - &[Coin { - denom: "ampWHALE".to_string(), - amount: Uint128::new(100_000_000u128), - }], - ) - .unwrap(); - - // add epochs to the fee distributor. - - // whale -> native - app.execute_contract( - creator.sender.clone(), - pair_tokens[0].clone(), - &pool_network::pair::ExecuteMsg::Swap { - offer_asset: Asset { - info: AssetInfo::NativeToken { - denom: "usdc".to_string(), - }, - amount: Uint128::new(200_000u128), - }, - belief_price: None, - max_spread: Some(Decimal::percent(40u64)), - to: None, - }, - &[Coin { - denom: "usdc".to_string(), - amount: Uint128::new(200_000u128), - }], - ) - .unwrap(); - - // advance the time to one day after the first epoch was created - app.set_block(BlockInfo { - height: 123456789u64, - time: Timestamp::from_nanos(1678888800_000000000u64), - chain_id: "".to_string(), - }); - - // Create new epoch, which triggers fee collection, aggregation and distribution - app.execute_contract( - creator.sender.clone(), - fee_distributor_address.clone(), - &NewEpoch {}, - &[], - ) - .unwrap(); - - // check that a new epoch was created - let expiring_epoch_res: EpochResponse = app - .wrap() - .query_wasm_smart( - fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::CurrentEpoch {}, - ) - .unwrap(); - assert_eq!(expiring_epoch_res.epoch.id, Uint64::one()); - assert_eq!( - expiring_epoch_res.epoch.available, - expiring_epoch_res.epoch.total - ); - assert!(expiring_epoch_res.epoch.claimed.is_empty()); - - // Create second epoch - app.execute_contract( - creator.sender.clone(), - pair_tokens[0].clone(), - &pool_network::pair::ExecuteMsg::Swap { - offer_asset: Asset { - info: AssetInfo::NativeToken { - denom: "usdc".to_string(), - }, - amount: Uint128::new(200_000u128), - }, - belief_price: None, - max_spread: Some(Decimal::percent(40u64)), - to: None, - }, - &[Coin { - denom: "usdc".to_string(), - amount: Uint128::new(200_000u128), - }], - ) - .unwrap(); - - // advance the time to one day after the first epoch was created - app.set_block(BlockInfo { - height: 123456789u64, - time: Timestamp::from_nanos(1678888800_000000000u64), - chain_id: "".to_string(), - }); - - // Create new epoch, which triggers fee collection, aggregation and distribution - app.execute_contract( - creator.sender.clone(), - fee_distributor_address.clone(), - &NewEpoch {}, - &[], - ) - .unwrap(); - - // check that the second epoch was created - let new_epoch_res: EpochResponse = app - .wrap() - .query_wasm_smart( - fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::CurrentEpoch {}, - ) - .unwrap(); - - assert_eq!(new_epoch_res.epoch.id, Uint64::new(2u64)); - assert_eq!(new_epoch_res.epoch.available, new_epoch_res.epoch.total); - assert!(new_epoch_res.epoch.claimed.is_empty()); - - // check what's claimable for both users - let claimable_epochs_res: ClaimableEpochsResponse = app - .wrap() - .query_wasm_smart( - fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::Claimable { - address: "creator".to_string(), - }, - ) - .unwrap(); - // should be able to claim 2 epochs - assert_eq!(claimable_epochs_res.epochs.len(), 2); - - let claimable_epochs_res: ClaimableEpochsResponse = app - .wrap() - .query_wasm_smart( - fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::Claimable { - address: "other".to_string(), - }, - ) - .unwrap(); - // should be able to claim 2 epochs - assert_eq!(claimable_epochs_res.epochs.len(), 2); - - // claim with creator - app.execute_contract( - creator.sender.clone(), - fee_distributor_address.clone(), - &white_whale::fee_distributor::ExecuteMsg::Claim {}, - &[], - ) - .unwrap(); - - let claimable_epochs_res: ClaimableEpochsResponse = app - .wrap() - .query_wasm_smart( - fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::Claimable { - address: "creator".to_string(), - }, - ) - .unwrap(); - // shouldn't be able to claim any more epochs - assert!(claimable_epochs_res.epochs.is_empty()); - - //query the epochs to check that something has been claimed - let epoch_res: EpochResponse = app - .wrap() - .query_wasm_smart( - fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::Epoch { - id: Uint64::new(2u64), - }, - ) - .unwrap(); - assert_eq!(epoch_res.epoch.id, Uint64::new(2u64)); - assert_eq!(epoch_res.epoch.claimed.len(), 1usize); - - let epoch_res: EpochResponse = app - .wrap() - .query_wasm_smart( - fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::Epoch { - id: Uint64::new(1u64), - }, - ) - .unwrap(); - assert_eq!(epoch_res.epoch.id, Uint64::new(1u64)); - assert_eq!(epoch_res.epoch.claimed.len(), 1usize); - - let _claimable_epochs_res: ClaimableEpochsResponse = app - .wrap() - .query_wasm_smart( - fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::Claimable { - address: "other".to_string(), + ], + slippage_tolerance: None, + receiver: None, }, + &[ + Coin { + denom: "uwhale".to_string(), + amount: Uint128::new(500_000u128), + }, + Coin { + denom: native_token.clone().to_string(), + amount: Uint128::new(500_000u128), + }, + ], ) .unwrap(); + } - // let's unbond just ampWHALE with creator, so it has bWHALE left. That would leave creator and other with 50% of the rewards each + // // "enable" bonding on 10 March 2023 15:00:00 + app.set_block(BlockInfo { + height: 123456789u64, + time: Timestamp::from_nanos(1678460400_000000000u64), + chain_id: "".to_string(), + }); + + // bond some tokens app.execute_contract( creator.sender.clone(), whale_lair_address.clone(), - &white_whale::whale_lair::ExecuteMsg::Unbond { + &white_whale::whale_lair::ExecuteMsg::Bond { asset: Asset { info: AssetInfo::NativeToken { - denom: "ampWHALE".to_string(), + denom: "bWHALE".to_string(), }, - amount: Uint128::new(200_000_000u128), + amount: Uint128::new(300_000_000u128), }, }, - &[], + &[Coin { + denom: "bWHALE".to_string(), + amount: Uint128::new(300_000_000u128), + }], ) .unwrap(); - // prepare to create a new epoch + // add epochs to the fee distributor. + + // whale -> native app.execute_contract( creator.sender.clone(), pair_tokens[0].clone(), @@ -8114,10 +8323,10 @@ fn user_weight_accounts_for_unbondings() { ) .unwrap(); - // advance the time + // advance the time until the point when the first epoch was created app.set_block(BlockInfo { height: 123456789u64, - time: Timestamp::from_nanos(1678978800_000000000u64), + time: Timestamp::from_nanos(1678888800_000000000u64), chain_id: "".to_string(), }); @@ -8130,143 +8339,28 @@ fn user_weight_accounts_for_unbondings() { ) .unwrap(); - // check that the second epoch was created - let new_epoch_res: EpochResponse = app + // check that a new epoch was created + let expiring_epoch_res: EpochResponse = app .wrap() .query_wasm_smart( fee_distributor_address.clone(), &white_whale::fee_distributor::QueryMsg::CurrentEpoch {}, ) .unwrap(); - assert_eq!(new_epoch_res.epoch.id, Uint64::new(3u64)); - assert_eq!(new_epoch_res.epoch.available, new_epoch_res.epoch.total); - assert!(new_epoch_res.epoch.claimed.is_empty()); - - let claimable_epochs_res: ClaimableEpochsResponse = app - .wrap() - .query_wasm_smart( - fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::Claimable { - address: "other".to_string(), - }, - ) - .unwrap(); - assert_eq!(claimable_epochs_res.epochs.len(), 3usize); - - let claimable_epochs_res: ClaimableEpochsResponse = app - .wrap() - .query_wasm_smart( - fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::Claimable { - address: "creator".to_string(), - }, - ) - .unwrap(); - assert_eq!(claimable_epochs_res.epochs.len(), 1usize); - - app.execute_contract( - Addr::unchecked("creator"), - fee_distributor_address.clone(), - &white_whale::fee_distributor::ExecuteMsg::Claim {}, - &[], - ) - .unwrap(); - let epoch_res: EpochResponse = app - .wrap() - .query_wasm_smart( - fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::Epoch { - id: Uint64::new(3u64), - }, - ) - .unwrap(); - assert_eq!(epoch_res.epoch.id, Uint64::new(3u64)); - // creator claimed 50% of the rewards - assert_eq!( - epoch_res.epoch.available, - vec![Asset { - info: NativeToken { - denom: "uwhale".to_string() - }, - amount: Uint128::new(1324u128) - }] - ); - assert_eq!( - epoch_res.epoch.claimed, - vec![Asset { - info: NativeToken { - denom: "uwhale".to_string() - }, - amount: Uint128::new(1323u128) - }] + println!( + "new epoch with good global_index:: {:?}", + expiring_epoch_res.epoch ); - //let's claim the rewards for other - app.execute_contract( - Addr::unchecked("other"), - fee_distributor_address.clone(), - &white_whale::fee_distributor::ExecuteMsg::Claim {}, - &[], - ) - .unwrap(); - - let epoch_res: EpochResponse = app - .wrap() - .query_wasm_smart( - fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::Epoch { - id: Uint64::new(3u64), - }, - ) - .unwrap(); - assert_eq!(epoch_res.epoch.id, Uint64::new(3u64)); - - assert_eq!( - epoch_res.epoch.available, - vec![Asset { - info: NativeToken { - denom: "uwhale".to_string() - }, - amount: Uint128::one() - }] - ); + assert_eq!(expiring_epoch_res.epoch.id, Uint64::one()); assert_eq!( - epoch_res.epoch.claimed, - vec![Asset { - info: NativeToken { - denom: "uwhale".to_string() - }, - amount: Uint128::new(2646u128) - }] - ); - assert!( - (epoch_res.epoch.claimed.first().unwrap().amount.u128() as i128 - - epoch_res.epoch.total.first().unwrap().amount.u128() as i128) - .abs() - < 2i128 + expiring_epoch_res.epoch.available, + expiring_epoch_res.epoch.total ); + assert!(expiring_epoch_res.epoch.claimed.is_empty()); - //now unbond partially, check if weight is computed correctly - app.execute_contract( - creator.sender.clone(), - whale_lair_address.clone(), - &white_whale::whale_lair::ExecuteMsg::Unbond { - asset: Asset { - info: AssetInfo::NativeToken { - denom: "bWHALE".to_string(), - }, - amount: Uint128::new(50_000_000u128), - }, - }, - &[], - ) - .unwrap(); - - // cretor -> 50bwhale -> 33% - // other -> 100bwhale -> 66% - - // prepare to create a new epoch + // Create second epoch app.execute_contract( creator.sender.clone(), pair_tokens[0].clone(), @@ -8288,13 +8382,39 @@ fn user_weight_accounts_for_unbondings() { ) .unwrap(); - // advance the time + // advance just a bit more after the new epoch can be created app.set_block(BlockInfo { height: 123456789u64, - time: Timestamp::from_nanos(1679065200_000000000u64), + time: Timestamp::from_nanos(1678888900_000000000u64), chain_id: "".to_string(), }); + // bond right after the next epoch start_time passed, but before the new epoch was created. + // should error because of the restriction we added on bonding/unbonding + let err = app + .execute_contract( + Addr::unchecked("other").clone(), + whale_lair_address.clone(), + &white_whale::whale_lair::ExecuteMsg::Bond { + asset: Asset { + info: AssetInfo::NativeToken { + denom: "ampWHALE".to_string(), + }, + amount: Uint128::new(100_000_000u128), + }, + }, + &[Coin { + denom: "ampWHALE".to_string(), + amount: Uint128::new(100_000_000u128), + }], + ) + .unwrap_err(); + + assert_eq!( + err.downcast::().unwrap(), + whale_lair::ContractError::NewEpochNotCreatedYet {} + ); + // Create new epoch, which triggers fee collection, aggregation and distribution app.execute_contract( creator.sender.clone(), @@ -8312,52 +8432,39 @@ fn user_weight_accounts_for_unbondings() { &white_whale::fee_distributor::QueryMsg::CurrentEpoch {}, ) .unwrap(); - assert_eq!(new_epoch_res.epoch.id, Uint64::new(4u64)); + + println!( + "new epoch with broken global_index:: {:?}", + new_epoch_res.epoch + ); + + assert_eq!(new_epoch_res.epoch.id, Uint64::new(2u64)); assert_eq!(new_epoch_res.epoch.available, new_epoch_res.epoch.total); assert!(new_epoch_res.epoch.claimed.is_empty()); - // now the creator has 33% of the total weight, and other has 66% + // bond now with other app.execute_contract( - Addr::unchecked("creator"), - fee_distributor_address.clone(), - &white_whale::fee_distributor::ExecuteMsg::Claim {}, - &[], - ) - .unwrap(); - - let epoch_res: EpochResponse = app - .wrap() - .query_wasm_smart( - fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::Epoch { - id: Uint64::new(4u64), - }, - ) - .unwrap(); - assert_eq!(epoch_res.epoch.id, Uint64::new(4u64)); - // creator claimed 33 % of the rewards - assert_eq!( - epoch_res.epoch.available, - vec![Asset { - info: NativeToken { - denom: "uwhale".to_string() - }, - amount: Uint128::new(1242u128) - }] - ); - assert_eq!( - epoch_res.epoch.claimed, - vec![Asset { - info: NativeToken { - denom: "uwhale".to_string() + Addr::unchecked("other").clone(), + whale_lair_address.clone(), + &white_whale::whale_lair::ExecuteMsg::Bond { + asset: Asset { + info: AssetInfo::NativeToken { + denom: "ampWHALE".to_string(), + }, + amount: Uint128::new(100_000_000u128), }, - amount: Uint128::new(620u128) - }] - ); + }, + &[Coin { + denom: "ampWHALE".to_string(), + amount: Uint128::new(100_000_000u128), + }], + ) + .unwrap(); } +#[cfg(not(feature = "osmosis"))] #[test] -fn users_can_claim_even_when_global_index_was_taken_after_epoch_was_created() { +fn collect_distribute_with_unbonders() { let creator = mock_creator(); let balances = vec![ ( @@ -8404,16 +8511,40 @@ fn users_can_claim_even_when_global_index_was_taken_after_epoch_was_created() { ) .unwrap(); + #[cfg(feature = "osmosis")] + let osmosis_fee_collector_address = app + .instantiate_contract( + fee_collector_id, + creator.clone().sender, + &InstantiateMsg {}, + &[], + "osmosis_fee_collector", + None, + ) + .unwrap(); + + #[cfg(not(feature = "osmosis"))] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + }; + + #[cfg(feature = "osmosis")] + let instantiate_msg = pool_network::factory::InstantiateMsg { + pair_code_id: pair_id, + trio_code_id: trio_id, + token_code_id: token_id, + fee_collector_addr: fee_collector_address.to_string(), + osmosis_fee_collector_addr: osmosis_fee_collector_address.to_string(), + }; + let pool_factory_address = app .instantiate_contract( pool_factory_id, creator.clone().sender, - &pool_network::factory::InstantiateMsg { - pair_code_id: pair_id, - trio_code_id: trio_id, - token_code_id: token_id, - fee_collector_addr: fee_collector_address.to_string(), - }, + &instantiate_msg, &[], "fee_collector", None, @@ -8454,7 +8585,7 @@ fn users_can_claim_even_when_global_index_was_taken_after_epoch_was_created() { whale_lair_id, creator.clone().sender, &white_whale::whale_lair::InstantiateMsg { - unbonding_period: Uint64::new(1_000_000_000_000u64), + unbonding_period: Uint64::new(1u64), growth_rate: Decimal::one(), bonding_assets: vec![ AssetInfo::NativeToken { @@ -8478,7 +8609,7 @@ fn users_can_claim_even_when_global_index_was_taken_after_epoch_was_created() { &white_whale::fee_distributor::InstantiateMsg { bonding_contract_addr: whale_lair_address.clone().to_string(), fee_collector_addr: fee_collector_address.clone().to_string(), - grace_period: Uint64::new(3u64), + grace_period: Uint64::new(1), epoch_config: EpochConfig { duration: Uint64::new(86_400_000_000_000u64), // a day genesis_epoch: Uint64::new(1678802400_000000000u64), // March 14, 2023 2:00:00 PM @@ -8506,6 +8637,7 @@ fn users_can_claim_even_when_global_index_was_taken_after_epoch_was_created() { &[], ) .unwrap(); + // add pool router address to the fee collector to be able to aggregate fees app.execute_contract( creator.sender.clone(), @@ -8664,35 +8796,298 @@ fn users_can_claim_even_when_global_index_was_taken_after_epoch_was_created() { .unwrap(); } - // // "enable" bonding on 10 March 2023 15:00:00 - app.set_block(BlockInfo { - height: 123456789u64, - time: Timestamp::from_nanos(1678460400_000000000u64), - chain_id: "".to_string(), - }); + // bond some tokens + app.execute_contract( + creator.sender.clone(), + whale_lair_address.clone(), + &white_whale::whale_lair::ExecuteMsg::Bond { + asset: Asset { + info: AssetInfo::NativeToken { + denom: "ampWHALE".to_string(), + }, + amount: Uint128::new(1_000u128), + }, + }, + &[Coin { + denom: "ampWHALE".to_string(), + amount: Uint128::new(1_000u128), + }], + ) + .unwrap(); + + app.execute_contract( + Addr::unchecked("other").clone(), + whale_lair_address.clone(), + &white_whale::whale_lair::ExecuteMsg::Bond { + asset: Asset { + info: AssetInfo::NativeToken { + denom: "ampWHALE".to_string(), + }, + amount: Uint128::new(1_000u128), + }, + }, + &[Coin { + denom: "ampWHALE".to_string(), + amount: Uint128::new(1_000u128), + }], + ) + .unwrap(); + + // Create some epochs, for the first one all good but for the second we will have an unbonding + + // Create EPOCH 1 with 100 whale + // whale -> native + app.execute_contract( + creator.sender.clone(), + pair_tokens[0].clone(), + &pool_network::pair::ExecuteMsg::Swap { + offer_asset: Asset { + info: AssetInfo::NativeToken { + denom: "usdc".to_string(), + }, + amount: Uint128::new(2_010u128), + }, + belief_price: None, + max_spread: None, + to: None, + }, + &[Coin { + denom: "usdc".to_string(), + amount: Uint128::new(2_010u128), + }], + ) + .unwrap(); + + // advance the time to one day after the first epoch was created + app.set_block(BlockInfo { + height: 123456789u64, + time: Timestamp::from_nanos(1678888800_000000000u64), + chain_id: "".to_string(), + }); + + // Create new epoch, which triggers fee collection, aggregation and distribution + // Verify epoch 1 + app.execute_contract( + creator.sender.clone(), + fee_distributor_address.clone(), + &NewEpoch {}, + &[], + ) + .unwrap(); + + // check that a new epoch was created + let expiring_epoch_res: EpochResponse = app + .wrap() + .query_wasm_smart( + fee_distributor_address.clone(), + &white_whale::fee_distributor::QueryMsg::CurrentEpoch {}, + ) + .unwrap(); + assert_eq!(expiring_epoch_res.epoch.id, Uint64::one()); + assert_eq!( + expiring_epoch_res.epoch.available, + expiring_epoch_res.epoch.total + ); + assert!(expiring_epoch_res.epoch.claimed.is_empty()); + // Verify expiring_epoch_res.epoch.available, has 100 whale as an Asset + assert_eq!( + expiring_epoch_res.epoch.available, + vec![Asset { + info: AssetInfo::NativeToken { + denom: "uwhale".to_string(), + }, + amount: Uint128::new(100u128), + }] + ); + + // for user in vec![creator.sender.clone(), Addr::unchecked("other")] { + // let weight: BondingWeightResponse = app + // .wrap() + // .query_wasm_smart( + // whale_lair_address.clone(), + // &white_whale::whale_lair::QueryMsg::Weight { + // address: user.to_string(), + // // timestamp: Some(Timestamp::from_nanos(1678888800_000000000u64-1)), + // global_weight: None, + // timestamp: None, + // }, + // ) + // .unwrap(); + // weights.push(weight.weight); + // } + // println!(" -> weights after each bonding 1000 whale: {:?}", weights); + + // When creating the second epoch, the first one will be expiring since the grace_period was set to 1. + // Make sure the available tokens on the expiring epoch are transferred to the second one. + app.execute_contract( + creator.sender.clone(), + pair_tokens[0].clone(), + &pool_network::pair::ExecuteMsg::Swap { + offer_asset: Asset { + info: AssetInfo::NativeToken { + denom: "usdc".to_string(), + }, + amount: Uint128::new(2_050u128), + }, + belief_price: None, + max_spread: None, + to: None, + }, + &[Coin { + denom: "usdc".to_string(), + amount: Uint128::new(2_050u128), + }], + ) + .unwrap(); + + // Get the weight of create.sender before bonding + // Get the weight after bonding ensure its different + let _weight_before: BondingWeightResponse = app + .wrap() + .query_wasm_smart( + whale_lair_address.clone(), + &white_whale::whale_lair::QueryMsg::Weight { + address: creator.sender.to_string(), + // timestamp: Some(Timestamp::from_nanos(1678888800_000000000u64-1)), + global_index: None, + timestamp: None, + }, + ) + .unwrap(); + + // advance the time to one day after the first epoch was created + app.set_block(BlockInfo { + height: 123456789u64, + time: Timestamp::from_nanos(1678888800_000000000u64), + chain_id: "".to_string(), + }); + + // Create new epoch, which triggers fee collection, aggregation and distribution + // Create EPOCH 2 + app.execute_contract( + creator.sender.clone(), + fee_distributor_address.clone(), + &NewEpoch {}, + &[], + ) + .unwrap(); + + // check that the second epoch was created + let new_epoch_res: EpochResponse = app + .wrap() + .query_wasm_smart( + fee_distributor_address.clone(), + &white_whale::fee_distributor::QueryMsg::CurrentEpoch {}, + ) + .unwrap(); + + assert_eq!(new_epoch_res.epoch.id, Uint64::new(2u64)); + assert_eq!(new_epoch_res.epoch.available, new_epoch_res.epoch.total); + assert!(new_epoch_res.epoch.claimed.is_empty()); + + // check that the available assets for the expired epoch are zero/empty + let expired_epoch_res: EpochResponse = app + .wrap() + .query_wasm_smart( + fee_distributor_address.clone(), + &white_whale::fee_distributor::QueryMsg::Epoch { id: Uint64::one() }, + ) + .unwrap(); + assert!(expired_epoch_res.epoch.available.is_empty()); + + // let weight_after: BondingWeightResponse = app + // .wrap() + // .query_wasm_smart( + // whale_lair_address.clone(), + // &white_whale::whale_lair::QueryMsg::Weight { + // address: creator.sender.to_string(), + // // timestamp: Some(Timestamp::from_nanos(1678888800_000000000u64-1)), + // global_weight: None, + // timestamp: None, + // }, + // ) + // .unwrap(); + + // Get weight of other user and ensure its lower than the first + // let user_two_weight: BondingWeightResponse = app + // .wrap() + // .query_wasm_smart( + // whale_lair_address.clone(), + // &white_whale::whale_lair::QueryMsg::Weight { + // address: Addr::unchecked("other").to_string(), + // // timestamp: Some(Timestamp::from_nanos(1678888800_000000000u64-1)), + // global_weight: None, + // timestamp: None, + // }, + // ) + // .unwrap(); + + // since the fees collected for the second epoch were the same for the first, the available + // assets for the second epoch should be twice the amount of the first + + // iterate the new_epoch_res.epoch.available and add up the amounts for each asset + let mut total_amount_new_epoch = Uint128::zero(); + for asset in new_epoch_res.epoch.available { + total_amount_new_epoch += asset.amount; + } + println!("total_amount_new_epoch: {}", total_amount_new_epoch); + let mut total_amount_expired = Uint128::zero(); + //checking against total since total and available where the same, but available is empty now + for asset in expired_epoch_res.epoch.total { + total_amount_expired += asset.amount; + } + println!("total_amount_expired: {}", total_amount_expired); + assert!(total_amount_new_epoch - total_amount_expired > Uint128::zero()); + + let mut user_1_claims: Vec = vec![]; + // let mut user_2_claims: Vec = vec![]; - // bond some tokens + // Get the balance of the sender before claiming + let uwhale_balance_before_claiming = app + .wrap() + .query_balance(creator.sender.clone(), "uwhale") + .unwrap() + .amount; + // claim some rewards + app.execute_contract( + creator.sender.clone(), + fee_distributor_address.clone(), + &white_whale::fee_distributor::ExecuteMsg::Claim {}, + &[], + ) + .unwrap(); + + // Get balance after claiming + let uwhale_balance_after_claiming = app + .wrap() + .query_balance(creator.sender.clone(), "uwhale") + .unwrap() + .amount; + // // Verify amount + // assert_eq!( + // uwhale_balance_after_claiming - + // uwhale_balance_before_claiming, Uint128::new(100) + // ); + user_1_claims.push(uwhale_balance_after_claiming - uwhale_balance_before_claiming); + + // Time to unbond with user 1 app.execute_contract( creator.sender.clone(), whale_lair_address.clone(), - &white_whale::whale_lair::ExecuteMsg::Bond { + &white_whale::whale_lair::ExecuteMsg::Unbond { asset: Asset { info: AssetInfo::NativeToken { - denom: "bWHALE".to_string(), + denom: "ampWHALE".to_string(), }, - amount: Uint128::new(300_000_000u128), + amount: Uint128::new(500u128), }, }, - &[Coin { - denom: "bWHALE".to_string(), - amount: Uint128::new(300_000_000u128), - }], + &[], ) .unwrap(); - // add epochs to the fee distributor. - - // whale -> native + // NOTE: Here is where we could check weights if we wanted too + // Make sure the available tokens on the expiring epoch are transferred to the second one. app.execute_contract( creator.sender.clone(), pair_tokens[0].clone(), @@ -8701,27 +9096,31 @@ fn users_can_claim_even_when_global_index_was_taken_after_epoch_was_created() { info: AssetInfo::NativeToken { denom: "usdc".to_string(), }, - amount: Uint128::new(200_000u128), + amount: Uint128::new(2_050u128), }, belief_price: None, - max_spread: Some(Decimal::percent(40u64)), + max_spread: None, to: None, }, &[Coin { denom: "usdc".to_string(), - amount: Uint128::new(200_000u128), + amount: Uint128::new(2_050u128), }], ) .unwrap(); - // advance the time until the point when the first epoch was created + // Now we can advance time, create a third epoch and check that the fees collected are + // distributed to the users + // advance the time to one day after the second epoch was created app.set_block(BlockInfo { height: 123456789u64, - time: Timestamp::from_nanos(1678888800_000000000u64), + time: Timestamp::from_nanos(3357777600_000000000u64), chain_id: "".to_string(), }); // Create new epoch, which triggers fee collection, aggregation and distribution + // Create EPOCH 3 + app.execute_contract( creator.sender.clone(), fee_distributor_address.clone(), @@ -8730,8 +9129,8 @@ fn users_can_claim_even_when_global_index_was_taken_after_epoch_was_created() { ) .unwrap(); - // check that a new epoch was created - let expiring_epoch_res: EpochResponse = app + // check that the third epoch was created + let new_epoch_res: EpochResponse = app .wrap() .query_wasm_smart( fee_distributor_address.clone(), @@ -8739,116 +9138,102 @@ fn users_can_claim_even_when_global_index_was_taken_after_epoch_was_created() { ) .unwrap(); - println!( - "new epoch with good global_index:: {:?}", - expiring_epoch_res.epoch - ); - - assert_eq!(expiring_epoch_res.epoch.id, Uint64::one()); - assert_eq!( - expiring_epoch_res.epoch.available, - expiring_epoch_res.epoch.total - ); - assert!(expiring_epoch_res.epoch.claimed.is_empty()); + assert_eq!(new_epoch_res.epoch.id, Uint64::new(3u64)); + assert_eq!(new_epoch_res.epoch.available, new_epoch_res.epoch.total); + assert!(new_epoch_res.epoch.claimed.is_empty()); - // Create second epoch - app.execute_contract( - creator.sender.clone(), - pair_tokens[0].clone(), - &pool_network::pair::ExecuteMsg::Swap { - offer_asset: Asset { - info: AssetInfo::NativeToken { - denom: "usdc".to_string(), - }, - amount: Uint128::new(200_000u128), + // check that the available assets for the expired epoch are zero/empty + let expired_epoch_res: EpochResponse = app + .wrap() + .query_wasm_smart( + fee_distributor_address.clone(), + &white_whale::fee_distributor::QueryMsg::Epoch { + id: Uint64::from(2u64), }, - belief_price: None, - max_spread: Some(Decimal::percent(40u64)), - to: None, - }, - &[Coin { - denom: "usdc".to_string(), - amount: Uint128::new(200_000u128), - }], - ) - .unwrap(); + ) + .unwrap(); + assert!(expired_epoch_res.epoch.available.is_empty()); - // advance just a bit more after the new epoch can be created + // Advance time one more time app.set_block(BlockInfo { height: 123456789u64, - time: Timestamp::from_nanos(1678888900_000000000u64), + time: Timestamp::from_nanos(503666400_000000000u64), chain_id: "".to_string(), }); - // bond right after the next epoch start_time passed, but before the new epoch was created. - // should error because of the restriction we added on bonding/unbonding - let err = app - .execute_contract( - Addr::unchecked("other").clone(), - whale_lair_address.clone(), - &white_whale::whale_lair::ExecuteMsg::Bond { - asset: Asset { - info: AssetInfo::NativeToken { - denom: "ampWHALE".to_string(), - }, - amount: Uint128::new(100_000_000u128), - }, - }, - &[Coin { - denom: "ampWHALE".to_string(), - amount: Uint128::new(100_000_000u128), - }], - ) - .unwrap_err(); + // We should have about triple the amount of fees collected in the third epoch + // compared to the first + // iterate the new_epoch_res.epoch.available and add up the amounts for each asset + let mut total_amount_new_epoch = Uint128::zero(); - assert_eq!( - err.downcast::().unwrap(), - whale_lair::ContractError::NewEpochNotCreatedYet {} - ); + for asset in new_epoch_res.epoch.available { + total_amount_new_epoch += asset.amount; + } + let mut total_amount_expired = Uint128::zero(); + //checking against total since total and available where the same, but available is empty now + for asset in expired_epoch_res.epoch.total { + total_amount_expired += asset.amount; + } + // assert!(total_amount_new_epoch - total_amount_expired > Uint128::zero()); - // Create new epoch, which triggers fee collection, aggregation and distribution + // // Query and verify what is due to user 1 + // let claimable: ClaimableEpochsResponse = app.wrap().query_wasm_smart(fee_distributor_address.clone(), &white_whale::fee_distributor::QueryMsg::Claimable { address: creator.sender.to_string() }).unwrap(); + // println!(" -> claimable: {:?}", claimable); + // claim some rewards + let uwhale_balance_before_claiming = app + .wrap() + .query_balance(Addr::unchecked("other"), "uwhale") + .unwrap() + .amount; + // Claim for user 2 app.execute_contract( - creator.sender.clone(), + Addr::unchecked("other"), fee_distributor_address.clone(), - &NewEpoch {}, + &white_whale::fee_distributor::ExecuteMsg::Claim {}, &[], ) .unwrap(); - // check that the second epoch was created - let new_epoch_res: EpochResponse = app + let uwhale_balance_after_claiming = app .wrap() - .query_wasm_smart( - fee_distributor_address.clone(), - &white_whale::fee_distributor::QueryMsg::CurrentEpoch {}, - ) - .unwrap(); + .query_balance(Addr::unchecked("other"), "uwhale") + .unwrap() + .amount; - println!( - "new epoch with broken global_index:: {:?}", - new_epoch_res.epoch - ); + let user_2_whale_received = uwhale_balance_after_claiming - uwhale_balance_before_claiming; + println!("-> User 2 whale_received: {}", user_2_whale_received); - assert_eq!(new_epoch_res.epoch.id, Uint64::new(2u64)); - assert_eq!(new_epoch_res.epoch.available, new_epoch_res.epoch.total); - assert!(new_epoch_res.epoch.claimed.is_empty()); + // Claim 2 + // claim some rewards + let uwhale_balance_before_claiming = app + .wrap() + .query_balance(creator.sender.clone(), "uwhale") + .unwrap() + .amount; - // bond now with other app.execute_contract( - Addr::unchecked("other").clone(), - whale_lair_address.clone(), - &white_whale::whale_lair::ExecuteMsg::Bond { - asset: Asset { - info: AssetInfo::NativeToken { - denom: "ampWHALE".to_string(), - }, - amount: Uint128::new(100_000_000u128), - }, - }, - &[Coin { - denom: "ampWHALE".to_string(), - amount: Uint128::new(100_000_000u128), - }], + creator.sender.clone(), + fee_distributor_address.clone(), + &white_whale::fee_distributor::ExecuteMsg::Claim {}, + &[], ) .unwrap(); + + let uwhale_balance_after_claiming = app + .wrap() + .query_balance(creator.sender.clone(), "uwhale") + .unwrap() + .amount; + + let user_1_whale_received = uwhale_balance_after_claiming - uwhale_balance_before_claiming; + println!("whale_received: {}", user_1_whale_received); + + // For these claims, the bonds and weights started the same + // No claims happened + // User 2 should have received more than user 1 as user 1 halfed their bond and thus their weight + // User 1 should not get an even share anymore considering all else stays the same + assert_eq!(user_2_whale_received, Uint128::new(133u128)); + assert_eq!(user_1_whale_received, Uint128::new(66u128)); + + assert_ne!(user_2_whale_received, user_1_whale_received); } diff --git a/contracts/liquidity_hub/pool-network/frontend_helper/src/tests/mock_instantiate.rs b/contracts/liquidity_hub/pool-network/frontend_helper/src/tests/mock_instantiate.rs index c0622a54..9fe35eaf 100644 --- a/contracts/liquidity_hub/pool-network/frontend_helper/src/tests/mock_instantiate.rs +++ b/contracts/liquidity_hub/pool-network/frontend_helper/src/tests/mock_instantiate.rs @@ -1,13 +1,15 @@ -use crate::tests::mock_info::mock_creator; -use crate::tests::store_code::fee_distributor_mock_contract; use cosmwasm_std::{to_binary, Addr, Decimal, Uint128, WasmMsg}; use cw20::Cw20Coin; use cw_multi_test::{App, Executor}; + use white_whale::{ fee::Fee, pool_network::asset::{Asset, AssetInfo, PairType}, }; +use crate::tests::mock_info::mock_creator; +use crate::tests::store_code::fee_distributor_mock_contract; + use super::{ mock_info::mock_admin, store_code::{ @@ -69,30 +71,68 @@ pub fn app_mock_instantiate(app: &mut App, pool_assets: [AssetInfo; 2]) -> AppIn .try_into() .unwrap(); + let instantiate_msg: white_whale::pool_network::pair::InstantiateMsg; + let pool_fee: white_whale::pool_network::pair::PoolFee; + #[cfg(not(feature = "osmosis"))] + { + pool_fee = white_whale::pool_network::pair::PoolFee { + burn_fee: Fee { + share: Decimal::zero(), + }, + protocol_fee: Fee { + share: Decimal::zero(), + }, + swap_fee: Fee { + share: Decimal::zero(), + }, + }; + + instantiate_msg = white_whale::pool_network::pair::InstantiateMsg { + token_factory_lp: false, + token_code_id: token_id, + pool_fees: pool_fee.clone(), + pair_type: PairType::ConstantProduct, + fee_collector_addr: "fee_collector_addr".to_string(), + asset_decimals: [6, 6], + asset_infos: pool_assets.clone(), + } + } + + #[cfg(feature = "osmosis")] + { + pool_fee = white_whale::pool_network::pair::PoolFee { + burn_fee: Fee { + share: Decimal::zero(), + }, + protocol_fee: Fee { + share: Decimal::zero(), + }, + swap_fee: Fee { + share: Decimal::zero(), + }, + osmosis_fee: Fee { + share: Decimal::zero(), + }, + }; + + instantiate_msg = white_whale::pool_network::pair::InstantiateMsg { + token_factory_lp: false, + token_code_id: token_id, + pool_fees: pool_fee.clone(), + pair_type: PairType::ConstantProduct, + fee_collector_addr: "fee_collector_addr".to_string(), + asset_decimals: [6, 6], + asset_infos: pool_assets.clone(), + osmosis_fee_collector_addr: "osmosis_fee_collector_addr".to_string(), + } + } + // create the pair let pair = app .instantiate_contract( pair_id, mock_admin().sender, - &white_whale::pool_network::pair::InstantiateMsg { - token_factory_lp: false, - token_code_id: token_id, - pool_fees: white_whale::pool_network::pair::PoolFee { - burn_fee: Fee { - share: Decimal::zero(), - }, - protocol_fee: Fee { - share: Decimal::zero(), - }, - swap_fee: Fee { - share: Decimal::zero(), - }, - }, - pair_type: PairType::ConstantProduct, - fee_collector_addr: "fee_collector_addr".to_string(), - asset_decimals: [6, 6], - asset_infos: pool_assets.clone(), - }, + &instantiate_msg, &[], "mock pair", None, diff --git a/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/feature_toggle.rs b/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/feature_toggle.rs index a7702104..71642542 100644 --- a/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/feature_toggle.rs +++ b/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/feature_toggle.rs @@ -11,6 +11,7 @@ use white_whale::pool_network::trio::{ Cw20HookMsg, ExecuteMsg, FeatureToggle, InstantiateMsg, PoolFee, }; +#[cfg(not(feature = "osmosis"))] #[test] fn test_feature_toggle_swap_disabled() { let mut deps = mock_dependencies(&[Coin { @@ -129,6 +130,7 @@ fn test_feature_toggle_swap_disabled() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn test_feature_toggle_withdrawals_disabled() { let mut deps = mock_dependencies(&[Coin { @@ -219,6 +221,7 @@ fn test_feature_toggle_withdrawals_disabled() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn test_feature_toggle_deposits_disabled() { let mut deps = mock_dependencies(&[Coin { diff --git a/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/protocol_fees.rs b/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/protocol_fees.rs index 1cc572d3..70932a54 100644 --- a/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/protocol_fees.rs +++ b/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/protocol_fees.rs @@ -12,6 +12,7 @@ use white_whale::pool_network::asset::{Asset, AssetInfo}; use white_whale::pool_network::mock_querier::mock_dependencies; use white_whale::pool_network::trio::{Cw20HookMsg, ExecuteMsg, InstantiateMsg, PoolFee}; +#[cfg(not(feature = "osmosis"))] #[test] fn test_protocol_fees() { let total_share = Uint128::from(60_000_000_000u128); @@ -207,6 +208,7 @@ fn test_protocol_fees() { ); } +#[cfg(not(feature = "osmosis"))] #[test] fn test_collect_protocol_fees_successful() { let total_share = Uint128::from(30_000_000_000u128); @@ -509,6 +511,7 @@ fn test_collect_protocol_fees_successful() { ); } +#[cfg(not(feature = "osmosis"))] #[test] fn test_collect_protocol_fees_successful_1_fee_only() { let total_share = Uint128::from(30_000_000_000u128); @@ -697,6 +700,7 @@ fn test_collect_protocol_fees_successful_1_fee_only() { ); } +#[cfg(not(feature = "osmosis"))] #[test] fn protocol_fees() { let protocol_fee = PoolFee { diff --git a/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/provide_liquidity.rs b/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/provide_liquidity.rs index f7a64d3c..a94c2b64 100644 --- a/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/provide_liquidity.rs +++ b/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/provide_liquidity.rs @@ -17,6 +17,7 @@ use white_whale::pool_network::denom::MsgMint; use white_whale::pool_network::mock_querier::mock_dependencies; use white_whale::pool_network::trio::{ExecuteMsg, InstantiateMsg, PoolFee}; +#[cfg(not(feature = "osmosis"))] #[test] fn provide_liquidity_cw20_lp() { let mut deps = mock_dependencies(&[Coin { @@ -686,6 +687,7 @@ fn provide_liquidity_token_factory_lp() { assert_eq!(bank_send_msg, bank_send_msg_expected); } +#[cfg(not(feature = "osmosis"))] #[test] fn provide_liquidity_zero_amount() { let mut deps = mock_dependencies(&[Coin { @@ -796,6 +798,7 @@ fn provide_liquidity_zero_amount() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn provide_liquidity_invalid_minimum_lp_amount() { let mut deps = mock_dependencies(&[Coin { diff --git a/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/queries.rs b/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/queries.rs index 16bc2f7a..161d2214 100644 --- a/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/queries.rs +++ b/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/queries.rs @@ -8,6 +8,7 @@ use white_whale::pool_network::asset::{Asset, AssetInfo}; use white_whale::pool_network::mock_querier::mock_dependencies; use white_whale::pool_network::trio::{InstantiateMsg, PoolFee, PoolResponse, QueryMsg}; +#[cfg(not(feature = "osmosis"))] #[test] fn test_simulations_asset_missmatch() { let mut deps = mock_dependencies(&[]); @@ -108,6 +109,7 @@ fn test_simulations_asset_missmatch() { ); } +#[cfg(not(feature = "osmosis"))] #[test] fn test_query_pool() { let total_share_amount = Uint128::from(111u128); diff --git a/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/swap.rs b/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/swap.rs index d674f55d..ad7cb49b 100644 --- a/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/swap.rs +++ b/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/swap.rs @@ -20,6 +20,7 @@ use white_whale::pool_network::trio::{ SimulationResponse, }; +#[cfg(not(feature = "osmosis"))] #[test] fn test_compute_swap_with_huge_pool_variance() { let offer_pool = Uint128::from(395451850234u128); @@ -52,6 +53,7 @@ fn test_compute_swap_with_huge_pool_variance() { ); } +#[cfg(not(feature = "osmosis"))] #[test] fn try_native_to_token() { let total_share = Uint128::from(30000000000u128); @@ -408,6 +410,7 @@ fn try_native_to_token() { ); } +#[cfg(not(feature = "osmosis"))] #[test] fn try_swap_invalid_token() { let total_share = Uint128::from(30000000000u128); @@ -519,6 +522,7 @@ fn try_swap_invalid_token() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn try_token_to_native() { let total_share = Uint128::from(20_000_000_000u128); @@ -895,6 +899,7 @@ fn try_token_to_native() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn test_swap_to_third_party() { let total_share = Uint128::from(30_000_000_000u128); diff --git a/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/testing.rs b/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/testing.rs index 6b718104..3611c6fb 100644 --- a/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/testing.rs +++ b/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/testing.rs @@ -24,6 +24,7 @@ use crate::error::ContractError; use crate::helpers::assert_slippage_tolerance; use crate::queries::query_trio_info; +#[cfg(not(feature = "osmosis"))] #[test] fn proper_initialization_cw20_lp() { let mut deps = mock_dependencies(&[]); @@ -331,6 +332,7 @@ fn intialize_with_burnable_token_factory_asset() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn test_initialization_invalid_fees() { let mut deps = mock_dependencies(&[]); @@ -381,6 +383,7 @@ fn test_initialization_invalid_fees() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn test_initialization_invalid_amp() { let mut deps = mock_dependencies(&[]); @@ -437,6 +440,7 @@ fn test_initialization_invalid_amp() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn can_migrate_contract() { let mut deps = mock_dependencies(&[]); @@ -612,6 +616,7 @@ fn test_max_spread() { .unwrap(); } +#[cfg(not(feature = "osmosis"))] #[test] fn test_update_config_unsuccessful() { let mut deps = mock_dependencies(&[]); @@ -704,6 +709,7 @@ fn test_update_config_unsuccessful() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn test_update_config_successful() { let mut deps = mock_dependencies(&[]); diff --git a/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/withdrawals.rs b/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/withdrawals.rs index 1dce1b4d..4ab72f02 100644 --- a/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/withdrawals.rs +++ b/contracts/liquidity_hub/pool-network/stableswap_3pool/src/tests/withdrawals.rs @@ -18,6 +18,7 @@ use white_whale::pool_network::denom::MsgBurn; use white_whale::pool_network::mock_querier::mock_dependencies; use white_whale::pool_network::trio::{Cw20HookMsg, ExecuteMsg, InstantiateMsg, PoolFee}; +#[cfg(not(feature = "osmosis"))] #[test] fn withdraw_liquidity_cw20_lp() { let mut deps = mock_dependencies(&[Coin { @@ -416,6 +417,7 @@ fn withdraw_liquidity_token_factory_lp_wrong_asset() { assert_eq!(err, ContractError::AssetMismatch {}); } +#[cfg(not(feature = "osmosis"))] #[test] fn test_withdrawal_unauthorized() { let mut deps = mock_dependencies(&[Coin { @@ -489,6 +491,7 @@ fn test_withdrawal_unauthorized() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn test_withdrawal_wrong_message() { let mut deps = mock_dependencies(&[Coin { diff --git a/contracts/liquidity_hub/pool-network/terraswap_factory/src/commands.rs b/contracts/liquidity_hub/pool-network/terraswap_factory/src/commands.rs index 3cae77ee..231a2b10 100644 --- a/contracts/liquidity_hub/pool-network/terraswap_factory/src/commands.rs +++ b/contracts/liquidity_hub/pool-network/terraswap_factory/src/commands.rs @@ -218,6 +218,7 @@ pub fn create_pair( } #[allow(unused_variables)] +#[allow(clippy::too_many_arguments)] /// Updates a trio config pub fn update_trio_config( deps: DepsMut, diff --git a/contracts/liquidity_hub/pool-network/terraswap_factory/src/testing.rs b/contracts/liquidity_hub/pool-network/terraswap_factory/src/testing.rs index 991753da..209d4867 100644 --- a/contracts/liquidity_hub/pool-network/terraswap_factory/src/testing.rs +++ b/contracts/liquidity_hub/pool-network/terraswap_factory/src/testing.rs @@ -32,6 +32,7 @@ use crate::state::{ fn proper_initialization() { let mut deps = mock_dependencies(&[]); + #[cfg(not(feature = "osmosis"))] let msg = InstantiateMsg { pair_code_id: 321u64, trio_code_id: 456u64, @@ -39,6 +40,15 @@ fn proper_initialization() { fee_collector_addr: "collector".to_string(), }; + #[cfg(feature = "osmosis")] + let msg = InstantiateMsg { + pair_code_id: 321u64, + trio_code_id: 456u64, + token_code_id: 123u64, + fee_collector_addr: "collector".to_string(), + osmosis_fee_collector_addr: "osmosis_fee_collector_addr".to_string(), + }; + let info = mock_info("addr0000", &[]); // we can just call .unwrap() to assert this was a success @@ -49,12 +59,18 @@ fn proper_initialization() { assert_eq!(123u64, config_res.token_code_id); assert_eq!(321u64, config_res.pair_code_id); assert_eq!("addr0000".to_string(), config_res.owner); + #[cfg(feature = "osmosis")] + assert_eq!( + "osmosis_fee_collector_addr".to_string(), + config_res.osmosis_fee_collector_addr + ); } #[test] fn can_migrate_contract() { let mut deps = mock_dependencies(&[]); + #[cfg(not(feature = "osmosis"))] let msg = InstantiateMsg { pair_code_id: 321u64, trio_code_id: 456u64, @@ -62,6 +78,15 @@ fn can_migrate_contract() { fee_collector_addr: "collector".to_string(), }; + #[cfg(feature = "osmosis")] + let msg = InstantiateMsg { + pair_code_id: 321u64, + trio_code_id: 456u64, + token_code_id: 123u64, + fee_collector_addr: "collector".to_string(), + osmosis_fee_collector_addr: "osmosis_fee_collector_addr".to_string(), + }; + let info = mock_info("addr0000", &[]); instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); @@ -79,6 +104,7 @@ fn can_migrate_contract() { fn update_config() { let mut deps = mock_dependencies(&[]); + #[cfg(not(feature = "osmosis"))] let msg = InstantiateMsg { pair_code_id: 321u64, trio_code_id: 456u64, @@ -86,6 +112,15 @@ fn update_config() { fee_collector_addr: "collector".to_string(), }; + #[cfg(feature = "osmosis")] + let msg = InstantiateMsg { + pair_code_id: 321u64, + trio_code_id: 456u64, + token_code_id: 123u64, + fee_collector_addr: "collector".to_string(), + osmosis_fee_collector_addr: "osmosis_fee_collector_addr".to_string(), + }; + let info = mock_info("addr0000", &[]); // we can just call .unwrap() to assert this was a success @@ -93,12 +128,24 @@ fn update_config() { // update owner let info = mock_info("addr0000", &[]); + + #[cfg(not(feature = "osmosis"))] + let msg = ExecuteMsg::UpdateConfig { + owner: Some("addr0001".to_string()), + pair_code_id: None, + trio_code_id: None, + token_code_id: None, + fee_collector_addr: None, + }; + + #[cfg(feature = "osmosis")] let msg = ExecuteMsg::UpdateConfig { owner: Some("addr0001".to_string()), pair_code_id: None, trio_code_id: None, token_code_id: None, fee_collector_addr: None, + osmosis_fee_collector_addr: Some("new_osmosis_fee_collector_addr".to_string()), }; let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); @@ -111,10 +158,16 @@ fn update_config() { assert_eq!(321u64, config_res.pair_code_id); assert_eq!("addr0001".to_string(), config_res.owner); assert_eq!("collector".to_string(), config_res.fee_collector_addr); + #[cfg(feature = "osmosis")] + assert_eq!( + "new_osmosis_fee_collector_addr".to_string(), + config_res.osmosis_fee_collector_addr + ); // update left items let env = mock_env(); let info = mock_info("addr0001", &[]); + #[cfg(not(feature = "osmosis"))] let msg = ExecuteMsg::UpdateConfig { owner: None, pair_code_id: Some(100u64), @@ -123,6 +176,16 @@ fn update_config() { fee_collector_addr: Some("new_collector".to_string()), }; + #[cfg(feature = "osmosis")] + let msg = ExecuteMsg::UpdateConfig { + owner: None, + pair_code_id: Some(100u64), + trio_code_id: Some(300u64), + token_code_id: Some(200u64), + fee_collector_addr: Some("new_collector".to_string()), + osmosis_fee_collector_addr: None, + }; + let res = execute(deps.as_mut(), env, info, msg).unwrap(); assert_eq!(0, res.messages.len()); @@ -138,12 +201,23 @@ fn update_config() { // Unauthorized err let env = mock_env(); let info = mock_info("addr0000", &[]); + #[cfg(not(feature = "osmosis"))] + let msg = ExecuteMsg::UpdateConfig { + owner: None, + fee_collector_addr: None, + pair_code_id: None, + trio_code_id: None, + token_code_id: None, + }; + + #[cfg(feature = "osmosis")] let msg = ExecuteMsg::UpdateConfig { owner: None, fee_collector_addr: None, pair_code_id: None, trio_code_id: None, token_code_id: None, + osmosis_fee_collector_addr: None, }; let res = execute(deps.as_mut(), env, info, msg); @@ -156,11 +230,21 @@ fn update_config() { fn init( mut deps: OwnedDeps, ) -> OwnedDeps { + #[cfg(not(feature = "osmosis"))] + let msg = InstantiateMsg { + pair_code_id: 321u64, + trio_code_id: 456u64, + token_code_id: 123u64, + fee_collector_addr: "collector".to_string(), + }; + + #[cfg(feature = "osmosis")] let msg = InstantiateMsg { pair_code_id: 321u64, trio_code_id: 456u64, token_code_id: 123u64, fee_collector_addr: "collector".to_string(), + osmosis_fee_collector_addr: "osmosis_fee_collector_addr".to_string(), }; let env = mock_env(); @@ -179,11 +263,21 @@ fn init( fn init_trio( mut deps: OwnedDeps, ) -> OwnedDeps { + #[cfg(not(feature = "osmosis"))] + let msg = InstantiateMsg { + pair_code_id: 321u64, + trio_code_id: 456u64, + token_code_id: 123u64, + fee_collector_addr: "collector".to_string(), + }; + + #[cfg(feature = "osmosis")] let msg = InstantiateMsg { pair_code_id: 321u64, trio_code_id: 456u64, token_code_id: 123u64, fee_collector_addr: "collector".to_string(), + osmosis_fee_collector_addr: "osmosis_fee_collector_addr".to_string(), }; let env = mock_env(); @@ -214,19 +308,38 @@ fn create_pair() { }, ]; + #[cfg(not(feature = "osmosis"))] + let pool_fees = PoolFee { + protocol_fee: Fee { + share: Decimal::percent(1u64), + }, + swap_fee: Fee { + share: Decimal::percent(1u64), + }, + burn_fee: Fee { + share: Decimal::zero(), + }, + }; + + #[cfg(feature = "osmosis")] + let pool_fees = PoolFee { + protocol_fee: Fee { + share: Decimal::percent(1u64), + }, + swap_fee: Fee { + share: Decimal::percent(1u64), + }, + burn_fee: Fee { + share: Decimal::zero(), + }, + osmosis_fee: Fee { + share: Decimal::percent(1u64), + }, + }; + let msg = ExecuteMsg::CreatePair { asset_infos: asset_infos.clone(), - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, + pool_fees: pool_fees.clone(), pair_type: PairType::ConstantProduct, token_factory_lp: false, }; @@ -249,6 +362,30 @@ fn create_pair() { attr("pair_type", "ConstantProduct"), ] ); + + #[cfg(not(feature = "osmosis"))] + let expected_msg = PairInstantiateMsg { + asset_infos: asset_infos.clone(), + token_code_id: 123u64, + asset_decimals: [6u8, 8u8], + pool_fees: pool_fees.clone(), + fee_collector_addr: "collector".to_string(), + pair_type: PairType::ConstantProduct, + token_factory_lp: false, + }; + + #[cfg(feature = "osmosis")] + let expected_msg = PairInstantiateMsg { + asset_infos: asset_infos.clone(), + token_code_id: 123u64, + asset_decimals: [6u8, 8u8], + pool_fees: pool_fees.clone(), + fee_collector_addr: "collector".to_string(), + pair_type: PairType::ConstantProduct, + token_factory_lp: false, + osmosis_fee_collector_addr: "osmosis_fee_collector_addr".to_string(), + }; + assert_eq!( res.messages, vec![SubMsg { @@ -256,26 +393,7 @@ fn create_pair() { gas_limit: None, reply_on: ReplyOn::Success, msg: WasmMsg::Instantiate { - msg: to_binary(&PairInstantiateMsg { - asset_infos: asset_infos.clone(), - token_code_id: 123u64, - asset_decimals: [6u8, 8u8], - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, - fee_collector_addr: "collector".to_string(), - pair_type: PairType::ConstantProduct, - token_factory_lp: false, - }) - .unwrap(), + msg: to_binary(&expected_msg).unwrap(), code_id: 321u64, funds: [Coin { denom: "uusd".to_string(), @@ -320,19 +438,38 @@ fn create_stableswap_pair() { }, ]; + #[cfg(not(feature = "osmosis"))] + let pool_fees = PoolFee { + protocol_fee: Fee { + share: Decimal::percent(1u64), + }, + swap_fee: Fee { + share: Decimal::percent(1u64), + }, + burn_fee: Fee { + share: Decimal::zero(), + }, + }; + + #[cfg(feature = "osmosis")] + let pool_fees = PoolFee { + protocol_fee: Fee { + share: Decimal::percent(1u64), + }, + swap_fee: Fee { + share: Decimal::percent(1u64), + }, + burn_fee: Fee { + share: Decimal::zero(), + }, + osmosis_fee: Fee { + share: Decimal::percent(1u64), + }, + }; + let msg = ExecuteMsg::CreatePair { asset_infos: asset_infos.clone(), - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, + pool_fees: pool_fees.clone(), pair_type: PairType::StableSwap { amp: 100 }, token_factory_lp: false, }; @@ -349,6 +486,30 @@ fn create_stableswap_pair() { attr("pair_type", "StableSwap"), ] ); + + #[cfg(not(feature = "osmosis"))] + let expected_msg = PairInstantiateMsg { + asset_infos: asset_infos.clone(), + token_code_id: 123u64, + asset_decimals: [6u8, 8u8], + pool_fees: pool_fees.clone(), + fee_collector_addr: "collector".to_string(), + pair_type: PairType::StableSwap { amp: 100 }, + token_factory_lp: false, + }; + + #[cfg(feature = "osmosis")] + let expected_msg = PairInstantiateMsg { + asset_infos: asset_infos.clone(), + token_code_id: 123u64, + asset_decimals: [6u8, 8u8], + pool_fees: pool_fees.clone(), + fee_collector_addr: "collector".to_string(), + pair_type: PairType::StableSwap { amp: 100 }, + token_factory_lp: false, + osmosis_fee_collector_addr: "osmosis_fee_collector_addr".to_string(), + }; + assert_eq!( res.messages, vec![SubMsg { @@ -356,26 +517,7 @@ fn create_stableswap_pair() { gas_limit: None, reply_on: ReplyOn::Success, msg: WasmMsg::Instantiate { - msg: to_binary(&PairInstantiateMsg { - asset_infos: asset_infos.clone(), - token_code_id: 123u64, - asset_decimals: [6u8, 8u8], - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, - fee_collector_addr: "collector".to_string(), - pair_type: PairType::StableSwap { amp: 100 }, - token_factory_lp: false, - }) - .unwrap(), + msg: to_binary(&expected_msg).unwrap(), code_id: 321u64, funds: vec![], label: "uusd-mAAPL pair".to_string(), @@ -432,19 +574,38 @@ fn create_pair_native_token_and_ibc_token() { }, ]; + #[cfg(not(feature = "osmosis"))] + let pool_fees = PoolFee { + protocol_fee: Fee { + share: Decimal::percent(1u64), + }, + swap_fee: Fee { + share: Decimal::percent(1u64), + }, + burn_fee: Fee { + share: Decimal::zero(), + }, + }; + + #[cfg(feature = "osmosis")] + let pool_fees = PoolFee { + protocol_fee: Fee { + share: Decimal::percent(1u64), + }, + swap_fee: Fee { + share: Decimal::percent(1u64), + }, + burn_fee: Fee { + share: Decimal::zero(), + }, + osmosis_fee: Fee { + share: Decimal::percent(1u64), + }, + }; + let msg = ExecuteMsg::CreatePair { asset_infos: asset_infos.clone(), - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, + pool_fees: pool_fees.clone(), pair_type: PairType::ConstantProduct, token_factory_lp: false, }; @@ -461,6 +622,30 @@ fn create_pair_native_token_and_ibc_token() { attr("pair_type", "ConstantProduct"), ] ); + + #[cfg(not(feature = "osmosis"))] + let expected_msg = PairInstantiateMsg { + asset_infos: asset_infos.clone(), + token_code_id: 123u64, + asset_decimals: [6u8, 6u8], + pool_fees: pool_fees.clone(), + fee_collector_addr: "collector".to_string(), + pair_type: PairType::ConstantProduct, + token_factory_lp: false, + }; + + #[cfg(feature = "osmosis")] + let expected_msg = PairInstantiateMsg { + asset_infos: asset_infos.clone(), + token_code_id: 123u64, + asset_decimals: [6u8, 6u8], + pool_fees: pool_fees.clone(), + fee_collector_addr: "collector".to_string(), + pair_type: PairType::ConstantProduct, + token_factory_lp: false, + osmosis_fee_collector_addr: "osmosis_fee_collector_addr".to_string(), + }; + assert_eq!( res.messages, vec![SubMsg { @@ -468,26 +653,7 @@ fn create_pair_native_token_and_ibc_token() { gas_limit: None, reply_on: ReplyOn::Success, msg: WasmMsg::Instantiate { - msg: to_binary(&PairInstantiateMsg { - asset_infos: asset_infos.clone(), - token_code_id: 123u64, - asset_decimals: [6u8, 6u8], - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, - fee_collector_addr: "collector".to_string(), - pair_type: PairType::ConstantProduct, - token_factory_lp: false, - }) - .unwrap(), + msg: to_binary(&expected_msg).unwrap(), code_id: 321u64, funds: vec![], label: "uusd-ibc/2739...5EB2 pair".to_string(), @@ -551,19 +717,38 @@ fn create_ibc_tokens_pair() { }, ]; + #[cfg(not(feature = "osmosis"))] + let pool_fees = PoolFee { + protocol_fee: Fee { + share: Decimal::percent(1u64), + }, + swap_fee: Fee { + share: Decimal::percent(1u64), + }, + burn_fee: Fee { + share: Decimal::zero(), + }, + }; + + #[cfg(feature = "osmosis")] + let pool_fees = PoolFee { + protocol_fee: Fee { + share: Decimal::percent(1u64), + }, + swap_fee: Fee { + share: Decimal::percent(1u64), + }, + burn_fee: Fee { + share: Decimal::zero(), + }, + osmosis_fee: Fee { + share: Decimal::percent(1u64), + }, + }; + let msg = ExecuteMsg::CreatePair { asset_infos: asset_infos.clone(), - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, + pool_fees: pool_fees.clone(), pair_type: PairType::ConstantProduct, token_factory_lp: false, }; @@ -580,6 +765,30 @@ fn create_ibc_tokens_pair() { attr("pair_type", "ConstantProduct"), ] ); + + #[cfg(not(feature = "osmosis"))] + let expected_msg = PairInstantiateMsg { + asset_infos: asset_infos.clone(), + token_code_id: 123u64, + asset_decimals: [6u8, 6u8], + pool_fees: pool_fees.clone(), + fee_collector_addr: "collector".to_string(), + pair_type: PairType::ConstantProduct, + token_factory_lp: false, + }; + + #[cfg(feature = "osmosis")] + let expected_msg = PairInstantiateMsg { + asset_infos: asset_infos.clone(), + token_code_id: 123u64, + asset_decimals: [6u8, 6u8], + pool_fees: pool_fees.clone(), + fee_collector_addr: "collector".to_string(), + pair_type: PairType::ConstantProduct, + token_factory_lp: false, + osmosis_fee_collector_addr: "osmosis_fee_collector_addr".to_string(), + }; + assert_eq!( res.messages, vec![SubMsg { @@ -587,26 +796,7 @@ fn create_ibc_tokens_pair() { gas_limit: None, reply_on: ReplyOn::Success, msg: WasmMsg::Instantiate { - msg: to_binary(&PairInstantiateMsg { - asset_infos: asset_infos.clone(), - token_code_id: 123u64, - asset_decimals: [6u8, 6u8], - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, - fee_collector_addr: "collector".to_string(), - pair_type: PairType::ConstantProduct, - token_factory_lp: false, - }) - .unwrap(), + msg: to_binary(&expected_msg).unwrap(), code_id: 321u64, funds: vec![], label: "ibc/4CD5...3D04-ibc/2739...5EB2 pair".to_string(), @@ -751,6 +941,7 @@ fn create_pair_ethereum_asset_and_ibc_token() { ); } +#[cfg(not(feature = "osmosis"))] #[test] fn fail_to_create_same_pair() { let mut deps = mock_dependencies(&[coin(10u128, "uusd".to_string())]); @@ -793,6 +984,7 @@ fn fail_to_create_same_pair() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn fail_to_create_existing_pair() { let mut deps = mock_dependencies(&[coin(10u128, "uusd".to_string())]); @@ -860,6 +1052,7 @@ fn fail_to_create_existing_pair() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn fail_to_create_pair_with_inactive_denoms() { let mut deps = mock_dependencies(&[coin(10u128, "uusd".to_string())]); @@ -902,6 +1095,7 @@ fn fail_to_create_pair_with_inactive_denoms() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn fail_to_create_pair_with_invalid_denom() { let mut deps = mock_dependencies(&[coin(10u128, "valid".to_string())]); @@ -982,6 +1176,7 @@ fn fail_to_create_pair_with_invalid_denom() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn fail_to_create_pair_with_unknown_token() { let mut deps = mock_dependencies(&[coin(10u128, "uusd".to_string())]); @@ -1036,6 +1231,7 @@ fn fail_to_create_pair_with_unknown_token() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn fail_to_create_pair_with_unknown_ibc_token() { let mut deps = mock_dependencies_with_balance(&[coin(10u128, "uusd".to_string())]); @@ -1285,6 +1481,7 @@ fn append_add_allow_native_token_with_already_exist_token() { assert_eq!(7u8, res.decimals) } +#[cfg(not(feature = "osmosis"))] #[test] fn execute_transactions_unauthorized() { let mut deps = mock_dependencies(&[coin(10u128, "uusd".to_string())]); @@ -1575,6 +1772,7 @@ fn delete_pair_failed_if_not_found() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn update_pair_config() { let mut deps = mock_dependencies(&[coin(10u128, "uusd".to_string())]); @@ -1630,6 +1828,7 @@ fn update_pair_config() { ); } +#[cfg(not(feature = "osmosis"))] #[test] fn create_trio_cw20_lp() { let mut deps = mock_dependencies(&[ @@ -1738,6 +1937,7 @@ fn create_trio_cw20_lp() { ); } +#[cfg(not(feature = "osmosis"))] #[test] fn create_trio_with_native_tokens_token_factory_lp() { let mut deps = mock_dependencies(&[ diff --git a/contracts/liquidity_hub/pool-network/terraswap_pair/src/commands.rs b/contracts/liquidity_hub/pool-network/terraswap_pair/src/commands.rs index 207325ea..372a5083 100644 --- a/contracts/liquidity_hub/pool-network/terraswap_pair/src/commands.rs +++ b/contracts/liquidity_hub/pool-network/terraswap_pair/src/commands.rs @@ -4,12 +4,6 @@ use cosmwasm_std::{ }; use cw20::{Cw20ExecuteMsg, Cw20ReceiveMsg}; -#[cfg(feature = "osmosis")] -use cosmwasm_std::Uint256; - -#[cfg(not(feature = "osmosis"))] -use cosmwasm_std::Decimal256; - #[cfg(any( feature = "token_factory", feature = "osmosis_token_factory", diff --git a/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/feature_toggle.rs b/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/feature_toggle.rs index 117b7352..fc86c2fd 100644 --- a/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/feature_toggle.rs +++ b/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/feature_toggle.rs @@ -11,6 +11,7 @@ use white_whale::pool_network::pair::{ Cw20HookMsg, ExecuteMsg, FeatureToggle, InstantiateMsg, PoolFee, }; +#[cfg(not(feature = "osmosis"))] #[test] fn test_feature_toggle_swap_disabled() { let mut deps = mock_dependencies(&[Coin { @@ -118,6 +119,7 @@ fn test_feature_toggle_swap_disabled() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn test_feature_toggle_withdrawals_disabled() { let mut deps = mock_dependencies(&[Coin { @@ -197,6 +199,7 @@ fn test_feature_toggle_withdrawals_disabled() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn test_feature_toggle_deposits_disabled() { let mut deps = mock_dependencies(&[Coin { diff --git a/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/protocol_fees.rs b/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/protocol_fees.rs index a979de45..ff1461c3 100644 --- a/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/protocol_fees.rs +++ b/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/protocol_fees.rs @@ -12,6 +12,7 @@ use white_whale::pool_network::asset::{Asset, AssetInfo, PairType}; use white_whale::pool_network::mock_querier::mock_dependencies; use white_whale::pool_network::pair::{Cw20HookMsg, ExecuteMsg, InstantiateMsg, PoolFee}; +#[cfg(not(feature = "osmosis"))] #[test] fn test_protocol_fees() { let total_share = Uint128::from(300_000_000_000u128); @@ -196,6 +197,7 @@ fn test_protocol_fees() { ); } +#[cfg(not(feature = "osmosis"))] #[test] fn test_collect_protocol_fees_successful() { let total_share = Uint128::from(300_000_000_000u128); @@ -424,6 +426,7 @@ fn test_collect_protocol_fees_successful() { ); } +#[cfg(not(feature = "osmosis"))] #[test] fn test_collect_protocol_fees_successful_1_fee_only() { let total_share = Uint128::from(300_000_000_000u128); @@ -603,6 +606,7 @@ fn test_collect_protocol_fees_successful_1_fee_only() { ); } +#[cfg(not(feature = "osmosis"))] #[test] fn protocol_fees() { let protocol_fee = PoolFee { @@ -682,3 +686,99 @@ fn protocol_fees() { }; assert_eq!(protocol_fee.is_valid(), Ok(())); } + +#[cfg(feature = "osmosis")] +#[test] +fn protocol_fees_osmosis() { + let protocol_fee = PoolFee { + protocol_fee: Fee { + share: Decimal::percent(50), + }, + swap_fee: Fee { + share: Decimal::percent(50), + }, + burn_fee: Fee { + share: Decimal::zero(), + }, + osmosis_fee: Fee { + share: Decimal::zero(), + }, + }; + assert_eq!( + protocol_fee.is_valid(), + Err(StdError::generic_err("Invalid fees")) + ); + + let protocol_fee = PoolFee { + protocol_fee: Fee { + share: Decimal::percent(200), + }, + swap_fee: Fee { + share: Decimal::percent(20), + }, + burn_fee: Fee { + share: Decimal::zero(), + }, + osmosis_fee: Fee { + share: Decimal::zero(), + }, + }; + assert_eq!( + protocol_fee.is_valid(), + Err(StdError::generic_err("Invalid fee")) + ); + + let protocol_fee = PoolFee { + protocol_fee: Fee { + share: Decimal::percent(20), + }, + swap_fee: Fee { + share: Decimal::percent(200), + }, + burn_fee: Fee { + share: Decimal::zero(), + }, + osmosis_fee: Fee { + share: Decimal::zero(), + }, + }; + assert_eq!( + protocol_fee.is_valid(), + Err(StdError::generic_err("Invalid fee")) + ); + + let protocol_fee = PoolFee { + protocol_fee: Fee { + share: Decimal::percent(40), + }, + swap_fee: Fee { + share: Decimal::percent(60), + }, + burn_fee: Fee { + share: Decimal::zero(), + }, + osmosis_fee: Fee { + share: Decimal::zero(), + }, + }; + assert_eq!( + protocol_fee.is_valid(), + Err(StdError::generic_err("Invalid fees")) + ); + + let protocol_fee = PoolFee { + protocol_fee: Fee { + share: Decimal::percent(20), + }, + swap_fee: Fee { + share: Decimal::percent(60), + }, + burn_fee: Fee { + share: Decimal::zero(), + }, + osmosis_fee: Fee { + share: Decimal::percent(10), + }, + }; + assert_eq!(protocol_fee.is_valid(), Ok(())); +} diff --git a/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/provide_liquidity.rs b/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/provide_liquidity.rs index 1d54b0f1..3b1155d5 100644 --- a/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/provide_liquidity.rs +++ b/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/provide_liquidity.rs @@ -30,6 +30,7 @@ use cosmwasm_std::coin; #[cfg(feature = "injective")] use cw_multi_test::Executor; +#[cfg(not(feature = "osmosis"))] #[test] fn provide_liquidity_cw20_lp() { let mut deps = mock_dependencies(&[Coin { @@ -500,6 +501,7 @@ fn provide_liquidity_cw20_lp() { let _res = execute(deps.as_mut(), env, info, msg).unwrap(); } +#[cfg(not(feature = "osmosis"))] #[test] fn provide_liquidity_zero_amount() { let mut deps = mock_dependencies(&[Coin { @@ -600,6 +602,7 @@ fn provide_liquidity_zero_amount() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn provide_liquidity_invalid_minimum_lp_amount() { let mut deps = mock_dependencies(&[Coin { diff --git a/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/queries.rs b/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/queries.rs index 29f85340..e2375687 100644 --- a/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/queries.rs +++ b/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/queries.rs @@ -8,6 +8,7 @@ use white_whale::pool_network::asset::{Asset, AssetInfo, PairType}; use white_whale::pool_network::mock_querier::mock_dependencies; use white_whale::pool_network::pair::{InstantiateMsg, PoolFee, PoolResponse, QueryMsg}; +#[cfg(not(feature = "osmosis"))] #[test] fn test_simulations_asset_missmatch() { let mut deps = mock_dependencies(&[]); @@ -89,6 +90,7 @@ fn test_simulations_asset_missmatch() { ); } +#[cfg(not(feature = "osmosis"))] #[test] fn test_query_pool() { let total_share_amount = Uint128::from(111u128); diff --git a/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/stableswap.rs b/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/stableswap.rs index d31b28b0..e5a5b3b6 100644 --- a/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/stableswap.rs +++ b/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/stableswap.rs @@ -44,6 +44,7 @@ mod tests { assert_eq!(y, Uint128::new(980_053)); } + #[cfg(not(feature = "osmosis"))] #[test] fn does_stableswap_correctly() { let asset_pool_amount = Uint128::from(990_050u128); @@ -83,6 +84,7 @@ mod tests { ); } + #[cfg(not(feature = "osmosis"))] #[test] fn does_stableswap_correctly_with_smaller_asset_pool() { // test when asset_pool is smaller than collateral_pool @@ -124,6 +126,7 @@ mod tests { ); } + #[cfg(not(feature = "osmosis"))] #[test] #[allow(clippy::inconsistent_digit_grouping)] fn does_stableswap_with_different_precisions() { @@ -204,6 +207,7 @@ mod tests { ); } + #[cfg(not(feature = "osmosis"))] #[test] #[allow(clippy::inconsistent_digit_grouping)] fn does_stableswap_with_18_18_precision() { diff --git a/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/swap.rs b/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/swap.rs index 4fa0a0ef..7dff31c8 100644 --- a/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/swap.rs +++ b/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/swap.rs @@ -1,16 +1,10 @@ -use crate::contract::{execute, instantiate, query, reply}; -use crate::error::ContractError; -use crate::helpers::compute_swap; -use crate::queries::query_fees; -use crate::state::{ - ALL_TIME_BURNED_FEES, ALL_TIME_COLLECTED_PROTOCOL_FEES, COLLECTED_PROTOCOL_FEES, -}; use cosmwasm_std::testing::{mock_env, mock_info, MOCK_CONTRACT_ADDR}; use cosmwasm_std::{ attr, coins, from_binary, to_binary, BankMsg, Coin, CosmosMsg, Decimal, Reply, ReplyOn, SubMsg, SubMsgResponse, SubMsgResult, Uint128, WasmMsg, }; use cw20::{Cw20ExecuteMsg, Cw20ReceiveMsg}; + use white_whale::fee::Fee; use white_whale::pool_network::asset::{Asset, AssetInfo, PairType}; use white_whale::pool_network::mock_querier::mock_dependencies; @@ -19,6 +13,14 @@ use white_whale::pool_network::pair::{ SimulationResponse, }; +use crate::contract::{execute, instantiate, query, reply}; +use crate::error::ContractError; +use crate::queries::query_fees; +use crate::state::{ + ALL_TIME_BURNED_FEES, ALL_TIME_COLLECTED_PROTOCOL_FEES, COLLECTED_PROTOCOL_FEES, +}; + +#[cfg(not(feature = "osmosis"))] #[test] fn test_compute_swap_with_huge_pool_variance() { let offer_pool = Uint128::from(395451850234u128); @@ -43,7 +45,7 @@ fn test_compute_swap_with_huge_pool_variance() { pool_fees, &PairType::ConstantProduct, 6, - 6 + 6, ) .unwrap() .return_amount, @@ -76,18 +78,43 @@ fn try_native_to_token() { ), ]); - let msg = InstantiateMsg { - asset_infos: [ - AssetInfo::NativeToken { - denom: "uusd".to_string(), + let msg: InstantiateMsg; + + #[cfg(not(feature = "osmosis"))] + { + let pool_fees = PoolFee { + protocol_fee: Fee { + share: Decimal::from_ratio(1u128, 1000u128), }, - AssetInfo::Token { - contract_addr: "asset0000".to_string(), + swap_fee: Fee { + share: Decimal::from_ratio(3u128, 1000u128), }, - ], - token_code_id: 10u64, - asset_decimals: [6u8, 8u8], - pool_fees: PoolFee { + burn_fee: Fee { + share: Decimal::from_ratio(1u128, 1000u128), + }, + }; + + msg = InstantiateMsg { + asset_infos: [ + AssetInfo::NativeToken { + denom: "uusd".to_string(), + }, + AssetInfo::Token { + contract_addr: "asset0000".to_string(), + }, + ], + token_code_id: 10u64, + asset_decimals: [6u8, 8u8], + pool_fees: pool_fees.clone(), + fee_collector_addr: "collector".to_string(), + pair_type: PairType::ConstantProduct, + token_factory_lp: false, + } + } + + #[cfg(feature = "osmosis")] + { + let pool_fees = PoolFee { protocol_fee: Fee { share: Decimal::from_ratio(1u128, 1000u128), }, @@ -97,11 +124,29 @@ fn try_native_to_token() { burn_fee: Fee { share: Decimal::from_ratio(1u128, 1000u128), }, - }, - fee_collector_addr: "collector".to_string(), - pair_type: PairType::ConstantProduct, - token_factory_lp: false, - }; + osmosis_fee: Fee { + share: Decimal::from_ratio(1u128, 1000u128), + }, + }; + + msg = InstantiateMsg { + asset_infos: [ + AssetInfo::NativeToken { + denom: "uusd".to_string(), + }, + AssetInfo::Token { + contract_addr: "asset0000".to_string(), + }, + ], + token_code_id: 10u64, + asset_decimals: [6u8, 8u8], + pool_fees: pool_fees.clone(), + fee_collector_addr: "collector".to_string(), + pair_type: PairType::ConstantProduct, + token_factory_lp: false, + osmosis_fee_collector_addr: "osmosis_fee_collector_addr".to_string(), + }; + } let env = mock_env(); let info = mock_info("addr0000", &[]); @@ -145,6 +190,9 @@ fn try_native_to_token() { }], ); let res = execute(deps.as_mut(), env, info, msg).unwrap(); + #[cfg(feature = "osmosis")] + assert_eq!(res.messages.len(), 3); + #[cfg(not(feature = "osmosis"))] assert_eq!(res.messages.len(), 2); let msg_transfer = res.messages.get(0).expect("no message"); @@ -158,30 +206,68 @@ fn try_native_to_token() { let expected_swap_fee_amount = expected_ret_amount.multiply_ratio(3u128, 1000u128); // 0.3% let expected_protocol_fee_amount = expected_ret_amount.multiply_ratio(1u128, 1000u128); // 0.1% let expected_burn_fee_amount = expected_ret_amount.multiply_ratio(1u128, 1000u128); // 0.1% - let expected_return_amount = expected_ret_amount - .checked_sub(expected_swap_fee_amount) - .unwrap() - .checked_sub(expected_protocol_fee_amount) - .unwrap() - .checked_sub(expected_burn_fee_amount) - .unwrap(); + #[cfg(feature = "osmosis")] + let expected_osmosis_fee_amount = expected_ret_amount.multiply_ratio(1u128, 1000u128); // 0.1% + + let expected_return_amount = { + let x = expected_ret_amount + .checked_sub(expected_swap_fee_amount) + .unwrap() + .checked_sub(expected_protocol_fee_amount) + .unwrap() + .checked_sub(expected_burn_fee_amount) + .unwrap(); + + #[cfg(feature = "osmosis")] + { + x.checked_sub(expected_osmosis_fee_amount).unwrap() + } - // since there is a burn_fee on the PoolFee, check burn message - // since we swapped to a cw20 token, the burn message should be a Cw20ExecuteMsg::Burn - let expected_burn_msg = SubMsg { - id: 0, - msg: CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: "asset0000".to_string(), - msg: to_binary(&Cw20ExecuteMsg::Burn { - amount: expected_burn_fee_amount, - }) - .unwrap(), - funds: vec![], - }), - gas_limit: None, - reply_on: ReplyOn::Never, + #[cfg(not(feature = "osmosis"))] + { + x + } }; - assert_eq!(res.messages.last().unwrap().clone(), expected_burn_msg); + + #[cfg(not(feature = "osmosis"))] + { + // since there is a burn_fee on the PoolFee, check burn message + // since we swapped to a cw20 token, the burn message should be a Cw20ExecuteMsg::Burn + let expected_burn_msg = SubMsg { + id: 0, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: "asset0000".to_string(), + msg: to_binary(&Cw20ExecuteMsg::Burn { + amount: expected_burn_fee_amount, + }) + .unwrap(), + funds: vec![], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }; + assert_eq!(res.messages.last().unwrap().clone(), expected_burn_msg); + } + + #[cfg(feature = "osmosis")] + { + // the last message is the osmosis fee send message + let expected_send_msg = SubMsg { + id: 0, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: "asset0000".to_string(), + msg: to_binary(&Cw20ExecuteMsg::Transfer { + recipient: "osmosis_fee_collector_addr".to_string(), + amount: expected_osmosis_fee_amount, + }) + .unwrap(), + funds: vec![], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }; + assert_eq!(res.messages.last().unwrap().clone(), expected_send_msg); + } // as we swapped native to token, we accumulate the protocol fees in token let protocol_fees_for_token = query_fees( @@ -288,6 +374,11 @@ fn try_native_to_token() { assert_eq!(expected_return_amount, simulation_res.return_amount); assert_eq!(expected_swap_fee_amount, simulation_res.swap_fee_amount); assert_eq!(expected_burn_fee_amount, simulation_res.burn_fee_amount); + #[cfg(feature = "osmosis")] + assert_eq!( + expected_osmosis_fee_amount, + simulation_res.osmosis_fee_amount + ); assert_eq!(expected_spread_amount, simulation_res.spread_amount); assert_eq!( expected_protocol_fee_amount, @@ -332,9 +423,10 @@ fn try_native_to_token() { ) .unwrap(); + println!("reverse_simulation_res: {:?}", reverse_simulation_res); assert!( (offer_amount.u128() as i128 - reverse_simulation_res.offer_amount.u128() as i128).abs() - < 3i128 + < 5i128 ); assert!( (expected_swap_fee_amount.u128() as i128 @@ -360,6 +452,13 @@ fn try_native_to_token() { .abs() < 3i128 ); + #[cfg(feature = "osmosis")] + assert!( + (expected_osmosis_fee_amount.u128() as i128 + - reverse_simulation_res.osmosis_fee_amount.u128() as i128) + .abs() + < 3i128 + ); assert_eq!( res.attributes, @@ -378,6 +477,11 @@ fn try_native_to_token() { expected_protocol_fee_amount.to_string(), ), attr("burn_fee_amount", expected_burn_fee_amount.to_string()), + #[cfg(feature = "osmosis")] + attr( + "osmosis_fee_amount", + expected_osmosis_fee_amount.to_string() + ), attr("swap_type", "ConstantProduct"), ] ); @@ -396,6 +500,7 @@ fn try_native_to_token() { ); } +#[cfg(not(feature = "osmosis"))] #[test] fn try_swap_invalid_token() { let total_share = Uint128::from(30000000000u128); @@ -523,18 +628,43 @@ fn try_token_to_native() { ), ]); - let msg = InstantiateMsg { - asset_infos: [ - AssetInfo::NativeToken { - denom: "uusd".to_string(), + let msg: InstantiateMsg; + + #[cfg(not(feature = "osmosis"))] + { + let pool_fees = PoolFee { + protocol_fee: Fee { + share: Decimal::from_ratio(1u128, 1000u128), }, - AssetInfo::Token { - contract_addr: "asset0000".to_string(), + swap_fee: Fee { + share: Decimal::from_ratio(3u128, 1000u128), }, - ], - token_code_id: 10u64, - asset_decimals: [8u8, 8u8], - pool_fees: PoolFee { + burn_fee: Fee { + share: Decimal::from_ratio(1u128, 1000u128), + }, + }; + + msg = InstantiateMsg { + asset_infos: [ + AssetInfo::NativeToken { + denom: "uusd".to_string(), + }, + AssetInfo::Token { + contract_addr: "asset0000".to_string(), + }, + ], + token_code_id: 10u64, + asset_decimals: [6u8, 8u8], + pool_fees: pool_fees.clone(), + fee_collector_addr: "collector".to_string(), + pair_type: PairType::ConstantProduct, + token_factory_lp: false, + } + } + + #[cfg(feature = "osmosis")] + { + let pool_fees = PoolFee { protocol_fee: Fee { share: Decimal::from_ratio(1u128, 1000u128), }, @@ -544,11 +674,29 @@ fn try_token_to_native() { burn_fee: Fee { share: Decimal::from_ratio(1u128, 1000u128), }, - }, - fee_collector_addr: "collector".to_string(), - pair_type: PairType::ConstantProduct, - token_factory_lp: false, - }; + osmosis_fee: Fee { + share: Decimal::from_ratio(1u128, 1000u128), + }, + }; + + msg = InstantiateMsg { + asset_infos: [ + AssetInfo::NativeToken { + denom: "uusd".to_string(), + }, + AssetInfo::Token { + contract_addr: "asset0000".to_string(), + }, + ], + token_code_id: 10u64, + asset_decimals: [6u8, 8u8], + pool_fees: pool_fees.clone(), + fee_collector_addr: "collector".to_string(), + pair_type: PairType::ConstantProduct, + token_factory_lp: false, + osmosis_fee_collector_addr: "osmosis_fee_collector_addr".to_string(), + }; + } let env = mock_env(); let info = mock_info("addr0000", &[]); @@ -606,6 +754,9 @@ fn try_token_to_native() { let info = mock_info("asset0000", &[]); let res = execute(deps.as_mut(), env, info, msg).unwrap(); + #[cfg(feature = "osmosis")] + assert_eq!(res.messages.len(), 3); + #[cfg(not(feature = "osmosis"))] assert_eq!(res.messages.len(), 2); let msg_transfer = res.messages.get(0).expect("no message"); @@ -619,25 +770,63 @@ fn try_token_to_native() { let expected_swap_fee_amount = expected_ret_amount.multiply_ratio(3u128, 1000u128); // 0.3% let expected_protocol_fee_amount = expected_ret_amount.multiply_ratio(1u128, 1000u128); // 0.1% let expected_burn_fee_amount = expected_ret_amount.multiply_ratio(1u128, 1000u128); // 0.1% - let expected_return_amount = expected_ret_amount - .checked_sub(expected_swap_fee_amount) - .unwrap() - .checked_sub(expected_protocol_fee_amount) - .unwrap() - .checked_sub(expected_burn_fee_amount) - .unwrap(); + #[cfg(feature = "osmosis")] + let expected_osmosis_fee_amount = expected_ret_amount.multiply_ratio(1u128, 1000u128); // 0.1% + + let expected_return_amount = { + let x = expected_ret_amount + .checked_sub(expected_swap_fee_amount) + .unwrap() + .checked_sub(expected_protocol_fee_amount) + .unwrap() + .checked_sub(expected_burn_fee_amount) + .unwrap(); + + #[cfg(feature = "osmosis")] + { + x.checked_sub(expected_osmosis_fee_amount).unwrap() + } - // since there is a burn_fee on the PoolFee, check burn message - // since we swapped to a native token, the burn message should be a BankMsg::Burn - let expected_burn_msg = SubMsg { - id: 0, - msg: CosmosMsg::Bank(BankMsg::Burn { - amount: coins(expected_burn_fee_amount.u128(), "uusd"), - }), - gas_limit: None, - reply_on: ReplyOn::Never, + #[cfg(not(feature = "osmosis"))] + { + x + } }; - assert_eq!(res.messages.last().unwrap().clone(), expected_burn_msg); + + #[cfg(not(feature = "osmosis"))] + { + // since there is a burn_fee on the PoolFee, check burn message + // since we swapped to a cw20 token, the burn message should be a Cw20ExecuteMsg::Burn + let expected_burn_msg = SubMsg { + id: 0, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: "asset0000".to_string(), + msg: to_binary(&Cw20ExecuteMsg::Burn { + amount: expected_burn_fee_amount, + }) + .unwrap(), + funds: vec![], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }; + assert_eq!(res.messages.last().unwrap().clone(), expected_burn_msg); + } + + #[cfg(feature = "osmosis")] + { + // the last message is the osmosis fee send message + let expected_send_msg = SubMsg { + id: 0, + msg: CosmosMsg::Bank(BankMsg::Send { + to_address: "osmosis_fee_collector_addr".to_string(), + amount: coins(expected_osmosis_fee_amount.u128().into(), "uusd"), + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }; + assert_eq!(res.messages.last().unwrap().clone(), expected_send_msg); + } // as we swapped token to native, we accumulate the protocol fees in native let protocol_fees_for_native = query_fees( @@ -720,6 +909,11 @@ fn try_token_to_native() { assert_eq!(expected_return_amount, simulation_res.return_amount); assert_eq!(expected_swap_fee_amount, simulation_res.swap_fee_amount); assert_eq!(expected_burn_fee_amount, simulation_res.burn_fee_amount); + #[cfg(feature = "osmosis")] + assert_eq!( + expected_osmosis_fee_amount, + simulation_res.osmosis_fee_amount + ); assert_eq!(expected_spread_amount, simulation_res.spread_amount); assert_eq!( expected_protocol_fee_amount, @@ -767,7 +961,7 @@ fn try_token_to_native() { assert!( (offer_amount.u128() as i128 - reverse_simulation_res.offer_amount.u128() as i128).abs() - < 3i128 + < 5i128 ); assert!( (expected_swap_fee_amount.u128() as i128 @@ -793,6 +987,13 @@ fn try_token_to_native() { .abs() < 3i128 ); + #[cfg(feature = "osmosis")] + assert!( + (expected_osmosis_fee_amount.u128() as i128 + - reverse_simulation_res.osmosis_fee_amount.u128() as i128) + .abs() + < 3i128 + ); assert_eq!( res.attributes, @@ -811,6 +1012,11 @@ fn try_token_to_native() { expected_protocol_fee_amount.to_string(), ), attr("burn_fee_amount", expected_burn_fee_amount.to_string()), + #[cfg(feature = "osmosis")] + attr( + "osmosis_fee_amount", + expected_osmosis_fee_amount.to_string() + ), attr("swap_type", "ConstantProduct"), ] ); @@ -846,6 +1052,7 @@ fn try_token_to_native() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn test_swap_to_third_party() { let total_share = Uint128::from(30_000_000_000u128); @@ -991,6 +1198,7 @@ fn test_swap_to_third_party() { assert_eq!(simulation_res.burn_fee_amount, Uint128::zero()); } +#[cfg(not(feature = "osmosis"))] #[test] fn stableswap_reverse_simulation() { let total_share = Uint128::from(1_000_000u128); @@ -1077,6 +1285,7 @@ fn stableswap_reverse_simulation() { ) } +#[cfg(not(feature = "osmosis"))] #[test] fn stableswap_with_different_precisions() { let total_share = Uint128::from(20_000_000_000u128); // 200_000.00000 @@ -1323,7 +1532,7 @@ fn stableswap_with_different_precisions() { expected_protocol_fee_amount.to_string(), ), attr("burn_fee_amount", expected_burn_fee_amount.to_string()), - attr("swap_type", "StableSwap") + attr("swap_type", "StableSwap"), ] ); @@ -1352,6 +1561,7 @@ fn stableswap_with_different_precisions() { ); } +#[cfg(not(feature = "osmosis"))] #[test] fn test_stableswap_with_no_swap_amount() { let offer_pool = Uint128::from(25000000u128); @@ -1376,7 +1586,7 @@ fn test_stableswap_with_no_swap_amount() { pool_fees, &PairType::ConstantProduct, 6, - 6 + 6, ) .unwrap() .return_amount, diff --git a/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/testing.rs b/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/testing.rs index 4105cb3d..fc339e38 100644 --- a/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/testing.rs +++ b/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/testing.rs @@ -1,29 +1,24 @@ use cosmwasm_std::testing::{mock_env, mock_info, MOCK_CONTRACT_ADDR}; #[cfg(feature = "token_factory")] use cosmwasm_std::CosmosMsg; -use cosmwasm_std::{ - from_binary, to_binary, Addr, Decimal, Reply, ReplyOn, StdError, SubMsg, SubMsgResponse, - SubMsgResult, Uint128, WasmMsg, -}; -use cw20::MinterResponse; +use cosmwasm_std::{from_binary, Addr, Decimal, Uint128}; use white_whale::fee::Fee; -use white_whale::pool_network::asset::{Asset, AssetInfo, PairInfo, PairType}; +use white_whale::pool_network::asset::{Asset, AssetInfo, PairType}; #[cfg(feature = "token_factory")] use white_whale::pool_network::denom::MsgCreateDenom; use white_whale::pool_network::mock_querier::mock_dependencies; use white_whale::pool_network::pair::ExecuteMsg::UpdateConfig; use white_whale::pool_network::pair::{Config, InstantiateMsg, PoolFee, QueryMsg}; use white_whale::pool_network::swap::assert_max_spread; -use white_whale::pool_network::token::InstantiateMsg as TokenInstantiateMsg; -use crate::contract::{execute, instantiate, query, reply}; +use crate::contract::{execute, instantiate, query}; use crate::error::ContractError; use crate::helpers::assert_slippage_tolerance; -use crate::queries::query_pair_info; #[cfg(feature = "token_factory")] use crate::state::LP_SYMBOL; +#[cfg(not(feature = "osmosis"))] #[test] fn proper_initialization_cw20_lp() { let mut deps = mock_dependencies(&[]); @@ -300,6 +295,7 @@ fn intialize_with_burnable_token_factory_asset() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn test_initialization_invalid_fees() { let mut deps = mock_dependencies(&[]); @@ -483,6 +479,7 @@ fn test_max_spread() { .unwrap_err(); } +#[cfg(not(feature = "osmosis"))] #[test] fn test_update_config_unsuccessful() { let mut deps = mock_dependencies(&[]); @@ -581,32 +578,75 @@ fn test_update_config_successful() { (&"asset0000".to_string(), &[]), ]); - let msg = InstantiateMsg { - asset_infos: [ - AssetInfo::NativeToken { - denom: "uusd".to_string(), + let msg: InstantiateMsg; + + #[cfg(not(feature = "osmosis"))] + { + let pool_fees = PoolFee { + protocol_fee: Fee { + share: Decimal::from_ratio(1u128, 1000u128), }, - AssetInfo::Token { - contract_addr: "asset0000".to_string(), + swap_fee: Fee { + share: Decimal::zero(), }, - ], - token_code_id: 10u64, - asset_decimals: [6u8, 8u8], - pool_fees: PoolFee { + burn_fee: Fee { + share: Decimal::from_ratio(1u128, 1000u128), + }, + }; + + msg = InstantiateMsg { + asset_infos: [ + AssetInfo::NativeToken { + denom: "uusd".to_string(), + }, + AssetInfo::Token { + contract_addr: "asset0000".to_string(), + }, + ], + token_code_id: 10u64, + asset_decimals: [6u8, 8u8], + pool_fees: pool_fees.clone(), + fee_collector_addr: "collector".to_string(), + pair_type: PairType::ConstantProduct, + token_factory_lp: false, + } + } + + #[cfg(feature = "osmosis")] + { + let pool_fees = PoolFee { protocol_fee: Fee { - share: Decimal::zero(), + share: Decimal::from_ratio(1u128, 1000u128), }, swap_fee: Fee { share: Decimal::zero(), }, burn_fee: Fee { - share: Decimal::zero(), + share: Decimal::from_ratio(1u128, 1000u128), }, - }, - fee_collector_addr: "collector".to_string(), - pair_type: PairType::ConstantProduct, - token_factory_lp: false, - }; + osmosis_fee: Fee { + share: Decimal::from_ratio(1u128, 1000u128), + }, + }; + + msg = InstantiateMsg { + asset_infos: [ + AssetInfo::NativeToken { + denom: "uusd".to_string(), + }, + AssetInfo::Token { + contract_addr: "asset0000".to_string(), + }, + ], + token_code_id: 10u64, + asset_decimals: [6u8, 8u8], + pool_fees: pool_fees.clone(), + fee_collector_addr: "collector".to_string(), + pair_type: PairType::ConstantProduct, + token_factory_lp: false, + osmosis_fee_collector_addr: "osmosis_fee_collector_addr".to_string(), + }; + } let env = mock_env(); let info = mock_info("addr0000", &[]); @@ -620,7 +660,19 @@ fn test_update_config_successful() { assert_eq!(config.owner, Addr::unchecked("addr0000")); assert!(config.feature_toggle.swaps_enabled); assert_eq!(config.pool_fees.swap_fee.share, Decimal::zero()); + #[cfg(feature = "osmosis")] + { + assert_eq!( + config.osmosis_fee_collector_addr, + Addr::unchecked("osmosis_fee_collector_addr") + ); + assert_eq!( + config.pool_fees.osmosis_fee.share, + Decimal::from_ratio(1u128, 1000u128) + ); + } + #[cfg(not(feature = "osmosis"))] let update_config_message = UpdateConfig { owner: Some("new_admin".to_string()), fee_collector_addr: Some("new_collector".to_string()), @@ -638,6 +690,28 @@ fn test_update_config_successful() { feature_toggle: None, }; + #[cfg(feature = "osmosis")] + let update_config_message = UpdateConfig { + owner: Some("new_admin".to_string()), + fee_collector_addr: Some("new_collector".to_string()), + pool_fees: Some(PoolFee { + protocol_fee: Fee { + share: Decimal::percent(1u64), + }, + swap_fee: Fee { + share: Decimal::percent(3u64), + }, + burn_fee: Fee { + share: Decimal::zero(), + }, + osmosis_fee: Fee { + share: Decimal::percent(5u64), + }, + }), + feature_toggle: None, + osmosis_fee_collector_addr: Some("new_osmosis_fee_collector_addr".to_string()), + }; + execute(deps.as_mut(), env, info, update_config_message).unwrap(); let config: Config = @@ -647,6 +721,14 @@ fn test_update_config_successful() { assert_eq!(config.owner, Addr::unchecked("new_admin")); assert_eq!(config.fee_collector_addr, Addr::unchecked("new_collector")); assert_eq!(config.pool_fees.swap_fee.share, Decimal::percent(3u64)); + #[cfg(feature = "osmosis")] + { + assert_eq!( + config.osmosis_fee_collector_addr, + Addr::unchecked("new_osmosis_fee_collector_addr") + ); + assert_eq!(config.pool_fees.osmosis_fee.share, Decimal::percent(5u64)); + } } #[test] diff --git a/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/withdrawals.rs b/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/withdrawals.rs index b6c785bd..a7d0308b 100644 --- a/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/withdrawals.rs +++ b/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/withdrawals.rs @@ -22,6 +22,7 @@ use crate::contract::{execute, instantiate, reply}; use crate::error::ContractError; use crate::state::{get_fees_for_asset, store_fee, COLLECTED_PROTOCOL_FEES}; +#[cfg(not(feature = "osmosis"))] #[test] fn withdraw_xyk_liquidity_cw20_lp() { let mut deps = mock_dependencies(&[Coin { @@ -184,6 +185,7 @@ fn withdraw_xyk_liquidity_cw20_lp() { ); } +#[cfg(not(feature = "osmosis"))] #[test] fn withdraw_stableswap_liquidity() { let mut deps = mock_dependencies(&[Coin { @@ -333,6 +335,7 @@ fn withdraw_stableswap_liquidity() { ); } +#[cfg(not(feature = "osmosis"))] #[test] fn test_withdrawal_unauthorized() { let mut deps = mock_dependencies(&[Coin { @@ -399,6 +402,7 @@ fn test_withdrawal_unauthorized() { } } +#[cfg(not(feature = "osmosis"))] #[test] fn test_withdrawal_wrong_message() { let mut deps = mock_dependencies(&[Coin { diff --git a/packages/white-whale/src/pool_network/asset.rs b/packages/white-whale/src/pool_network/asset.rs index e3823c93..634bcec9 100644 --- a/packages/white-whale/src/pool_network/asset.rs +++ b/packages/white-whale/src/pool_network/asset.rs @@ -231,7 +231,7 @@ impl AssetInfo { #[cfg(feature = "injective")] { if is_ethereum_bridged_asset(&denom) { - return get_ethereum_bridged_asset_label(denom.clone()); + return get_ethereum_bridged_asset_label(denom); } } if is_ibc_token(&denom) {