diff --git a/Cargo.lock b/Cargo.lock index 3719a617..c7293902 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -509,7 +509,7 @@ dependencies = [ [[package]] name = "fee_collector" -version = "1.1.2" +version = "1.1.4" dependencies = [ "cosmwasm-schema", "cosmwasm-std", diff --git a/contracts/liquidity_hub/fee_collector/Cargo.toml b/contracts/liquidity_hub/fee_collector/Cargo.toml index 50b87a89..14d253d3 100644 --- a/contracts/liquidity_hub/fee_collector/Cargo.toml +++ b/contracts/liquidity_hub/fee_collector/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fee_collector" -version = "1.1.2" +version = "1.1.4" authors = ["Kerber0x "] edition.workspace = true description = "Contract to collect the fees accrued by the pools and vaults in the liquidity hub" diff --git a/contracts/liquidity_hub/fee_collector/schema/fee_collector.json b/contracts/liquidity_hub/fee_collector/schema/fee_collector.json index c748c1ce..8552cba1 100644 --- a/contracts/liquidity_hub/fee_collector/schema/fee_collector.json +++ b/contracts/liquidity_hub/fee_collector/schema/fee_collector.json @@ -35,7 +35,7 @@ "additionalProperties": false }, { - "description": "Swaps the assets (fees) sitting in the fee collector into the given [AssetInfo] if possible. A [SwapRoute] should be available at the router to be able to make the swaps.", + "description": "Swaps the assets (fees) sitting in the fee collector into the distribution asset set by the fee collector. A [SwapRoute] should be available at the router to be able to make the swaps.", "type": "object", "required": [ "aggregate_fees" @@ -44,15 +44,11 @@ "aggregate_fees": { "type": "object", "required": [ - "aggregate_fees_for", - "asset_info" + "aggregate_fees_for" ], "properties": { "aggregate_fees_for": { "$ref": "#/definitions/FeesFor" - }, - "asset_info": { - "$ref": "#/definitions/AssetInfo" } }, "additionalProperties": false diff --git a/contracts/liquidity_hub/fee_collector/schema/raw/execute.json b/contracts/liquidity_hub/fee_collector/schema/raw/execute.json index 4f1dede2..840dd6b2 100644 --- a/contracts/liquidity_hub/fee_collector/schema/raw/execute.json +++ b/contracts/liquidity_hub/fee_collector/schema/raw/execute.json @@ -25,7 +25,7 @@ "additionalProperties": false }, { - "description": "Swaps the assets (fees) sitting in the fee collector into the given [AssetInfo] if possible. A [SwapRoute] should be available at the router to be able to make the swaps.", + "description": "Swaps the assets (fees) sitting in the fee collector into the distribution asset set by the fee collector. A [SwapRoute] should be available at the router to be able to make the swaps.", "type": "object", "required": [ "aggregate_fees" @@ -34,15 +34,11 @@ "aggregate_fees": { "type": "object", "required": [ - "aggregate_fees_for", - "asset_info" + "aggregate_fees_for" ], "properties": { "aggregate_fees_for": { "$ref": "#/definitions/FeesFor" - }, - "asset_info": { - "$ref": "#/definitions/AssetInfo" } }, "additionalProperties": false diff --git a/contracts/liquidity_hub/fee_collector/src/commands.rs b/contracts/liquidity_hub/fee_collector/src/commands.rs index bfa14771..44047ff3 100644 --- a/contracts/liquidity_hub/fee_collector/src/commands.rs +++ b/contracts/liquidity_hub/fee_collector/src/commands.rs @@ -13,24 +13,13 @@ use white_whale::pool_network::router::SwapOperation; use white_whale::vault_network::vault_factory::VaultsResponse; use crate::contract::{FEES_AGGREGATION_REPLY_ID, FEES_COLLECTION_REPLY_ID}; +use crate::queries::query_distribution_asset; use crate::state::{read_temporal_asset_infos, store_temporal_asset_info, CONFIG, TMP_EPOCH}; use crate::ContractError; /// Collects fees accrued by the pools and vaults. If a factory is provided then it only collects the /// fees from its children. -pub fn collect_fees( - deps: DepsMut, - info: MessageInfo, - env: Env, - collect_fees_for: FeesFor, -) -> Result { - let config: Config = CONFIG.load(deps.storage)?; - - // only the owner or the contract itself can aggregate the fees - if info.sender != config.owner && info.sender != env.contract.address { - return Err(ContractError::Unauthorized {}); - } - +pub fn collect_fees(deps: DepsMut, collect_fees_for: FeesFor) -> Result { let mut collect_fees_messages: Vec = Vec::new(); match collect_fees_for { @@ -170,17 +159,13 @@ pub fn update_config( /// Aggregates the fees collected into the given asset_info. pub fn aggregate_fees( mut deps: DepsMut, - info: MessageInfo, env: Env, - ask_asset_info: AssetInfo, aggregate_fees_for: FeesFor, ) -> Result { let config: Config = CONFIG.load(deps.storage)?; - // only the owner or the contract itself can aggregate the fees - if info.sender != config.owner && info.sender != env.contract.address { - return Err(ContractError::Unauthorized {}); - } + // query fee collector to get the distribution asset + let ask_asset_info = query_distribution_asset(deps.as_ref())?; let mut aggregate_fees_messages: Vec = Vec::new(); @@ -335,7 +320,6 @@ pub fn forward_fees( info: MessageInfo, env: Env, epoch: Epoch, - forward_fees_as: AssetInfo, ) -> Result { let config = CONFIG.load(deps.storage)?; @@ -392,7 +376,6 @@ pub fn forward_fees( contract_addr: env.contract.address.to_string(), funds: vec![], msg: to_binary(&ExecuteMsg::AggregateFees { - asset_info: forward_fees_as.clone(), aggregate_fees_for: FeesFor::Factory { factory_addr: config.vault_factory.to_string(), factory_type: FactoryType::Vault { @@ -412,7 +395,6 @@ pub fn forward_fees( contract_addr: env.contract.address.to_string(), funds: vec![], msg: to_binary(&ExecuteMsg::AggregateFees { - asset_info: forward_fees_as.clone(), aggregate_fees_for: FeesFor::Factory { factory_addr: config.pool_factory.to_string(), factory_type: FactoryType::Pool { @@ -432,7 +414,7 @@ pub fn forward_fees( messages.push(pools_fee_aggregation_msg); // saving the epoch and the asset info to forward the fees as in temp storage - TMP_EPOCH.save(deps.storage, &(epoch, forward_fees_as))?; + TMP_EPOCH.save(deps.storage, &epoch)?; Ok(Response::new() .add_attribute("action", "forward_fees") diff --git a/contracts/liquidity_hub/fee_collector/src/contract.rs b/contracts/liquidity_hub/fee_collector/src/contract.rs index bcdf0420..f6e0dfd4 100644 --- a/contracts/liquidity_hub/fee_collector/src/contract.rs +++ b/contracts/liquidity_hub/fee_collector/src/contract.rs @@ -13,6 +13,7 @@ use white_whale::fee_collector::{ use white_whale::pool_network::asset::{Asset, AssetInfo, ToCoins}; use crate::error::ContractError; +use crate::queries::query_distribution_asset; use crate::state::{CONFIG, TMP_EPOCH}; use crate::ContractError::MigrateInvalidVersion; use crate::{commands, migrations, queries}; @@ -49,10 +50,12 @@ pub fn instantiate( #[entry_point] pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result { if msg.id == FEES_AGGREGATION_REPLY_ID { - let (mut epoch, asset_info) = TMP_EPOCH + let mut epoch = TMP_EPOCH .may_load(deps.storage)? .ok_or(ContractError::CannotReadEpoch {})?; + let asset_info = query_distribution_asset(deps.as_ref())?; + let token_balance: Uint128 = match asset_info.clone() { AssetInfo::Token { .. } => { return Err(ContractError::InvalidContractsFeeAggregation {}) @@ -108,7 +111,7 @@ pub fn execute( ) -> Result { match msg { ExecuteMsg::CollectFees { collect_fees_for } => { - commands::collect_fees(deps, info, env, collect_fees_for) + commands::collect_fees(deps, collect_fees_for) } ExecuteMsg::UpdateConfig { owner, @@ -125,14 +128,10 @@ pub fn execute( pool_factory, vault_factory, ), - ExecuteMsg::AggregateFees { - asset_info, - aggregate_fees_for, - } => commands::aggregate_fees(deps, info, env, asset_info, aggregate_fees_for), - ExecuteMsg::ForwardFees { - epoch, - forward_fees_as, - } => commands::forward_fees(deps, info, env, epoch, forward_fees_as), + ExecuteMsg::AggregateFees { aggregate_fees_for } => { + commands::aggregate_fees(deps, env, aggregate_fees_for) + } + ExecuteMsg::ForwardFees { epoch, .. } => commands::forward_fees(deps, info, env, epoch), } } diff --git a/contracts/liquidity_hub/fee_collector/src/queries.rs b/contracts/liquidity_hub/fee_collector/src/queries.rs index 5dd6ca21..fb436ee3 100644 --- a/contracts/liquidity_hub/fee_collector/src/queries.rs +++ b/contracts/liquidity_hub/fee_collector/src/queries.rs @@ -2,7 +2,7 @@ use cosmwasm_std::{to_binary, Addr, Deps, QueryRequest, StdResult, WasmQuery}; use white_whale::fee_collector::{Config, ContractType, FactoryType, FeesFor}; use white_whale::pool_network; -use white_whale::pool_network::asset::Asset; +use white_whale::pool_network::asset::{Asset, AssetInfo}; use white_whale::pool_network::factory::PairsResponse; use white_whale::pool_network::pair::ProtocolFeesResponse as ProtocolPairFeesResponse; use white_whale::vault_network::vault::ProtocolFeesResponse as ProtocolVaultFeesResponse; @@ -143,3 +143,16 @@ fn query_fees_for_factory( Ok(fees) } + +/// Queries the fee collector to get the distribution asset +pub(crate) fn query_distribution_asset(deps: Deps) -> StdResult { + let config: Config = CONFIG.load(deps.storage)?; + + let fee_distributor_config: white_whale::fee_distributor::Config = + deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: config.fee_distributor.to_string(), + msg: to_binary(&white_whale::fee_distributor::QueryMsg::Config {})?, + }))?; + + Ok(fee_distributor_config.distribution_asset) +} diff --git a/contracts/liquidity_hub/fee_collector/src/state.rs b/contracts/liquidity_hub/fee_collector/src/state.rs index 2026a838..cfc6dadf 100644 --- a/contracts/liquidity_hub/fee_collector/src/state.rs +++ b/contracts/liquidity_hub/fee_collector/src/state.rs @@ -6,7 +6,7 @@ use white_whale::pool_network::asset::AssetInfo; pub const CONFIG: Item = Item::new("config"); pub const TMP_ASSET_INFOS: Map = Map::new("tmp_asset_infos"); -pub const TMP_EPOCH: Item<(Epoch, AssetInfo)> = Item::new("tmp_epoch"); +pub const TMP_EPOCH: Item = Item::new("tmp_epoch"); pub fn store_temporal_asset_info(deps: DepsMut, asset_info: AssetInfo) -> StdResult<()> { let key = asset_info diff --git a/contracts/liquidity_hub/fee_collector/src/tests/integration.rs b/contracts/liquidity_hub/fee_collector/src/tests/integration.rs index fdb11861..a953f8f1 100644 --- a/contracts/liquidity_hub/fee_collector/src/tests/integration.rs +++ b/contracts/liquidity_hub/fee_collector/src/tests/integration.rs @@ -43,6 +43,7 @@ fn collect_all_factories_cw20_fees_successfully() { let creator = mock_creator(); let fee_collector_id = store_fee_collector_code(&mut app); + let fee_distributor_id = store_fee_distributor_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); @@ -380,6 +381,43 @@ fn collect_all_factories_cw20_fees_successfully() { let ask_asset = AssetInfo::Token { contract_addr: cw20_tokens[0].to_string(), }; + + // init fee distributor + + let fee_distributor_address = app + .instantiate_contract( + fee_distributor_id, + creator.clone().sender, + &white_whale::fee_distributor::InstantiateMsg { + bonding_contract_addr: "whale_lair".clone().to_string(), + fee_collector_addr: fee_collector_address.clone().to_string(), + 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 + }, + distribution_asset: ask_asset.clone(), + }, + &[], + "fee_distributor", + None, + ) + .unwrap(); + + app.execute_contract( + creator.sender.clone(), + fee_collector_address.clone(), + &white_whale::fee_collector::ExecuteMsg::UpdateConfig { + owner: None, + pool_router: None, + fee_distributor: Some(fee_distributor_address.to_string()), + pool_factory: None, + vault_factory: None, + }, + &[], + ) + .unwrap(); + let mut ask_asset_original_balance = Uint128::zero(); for (asset_addr, asset) in assets_collected.clone() { let balance_res: BalanceResponse = app @@ -458,7 +496,6 @@ fn collect_all_factories_cw20_fees_successfully() { creator.sender, fee_collector_address.clone(), &AggregateFees { - asset_info: ask_asset, aggregate_fees_for: FeesFor::Factory { factory_addr: pool_factory_address.to_string(), factory_type: FactoryType::Pool { @@ -941,6 +978,7 @@ fn collect_pools_native_fees_successfully() { 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 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); @@ -1285,6 +1323,41 @@ fn collect_pools_native_fees_successfully() { let ask_asset = AssetInfo::Token { contract_addr: cw20_tokens[0].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".clone().to_string(), + fee_collector_addr: fee_collector_address.clone().to_string(), + 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 + }, + distribution_asset: ask_asset.clone(), + }, + &[], + "fee_distributor", + None, + ) + .unwrap(); + + app.execute_contract( + creator.sender.clone(), + fee_collector_address.clone(), + &white_whale::fee_collector::ExecuteMsg::UpdateConfig { + owner: None, + pool_router: None, + fee_distributor: Some(fee_distributor_address.to_string()), + pool_factory: None, + vault_factory: None, + }, + &[], + ) + .unwrap(); + let mut ask_asset_original_balance = Uint128::zero(); for (asset_id, asset) in assets_collected.clone() { if asset_id == "native" { @@ -1435,7 +1508,6 @@ fn collect_pools_native_fees_successfully() { creator.sender, fee_collector_address.clone(), &AggregateFees { - asset_info: ask_asset, aggregate_fees_for: FeesFor::Factory { factory_addr: pool_factory_address.to_string(), factory_type: FactoryType::Pool { @@ -2154,6 +2226,7 @@ fn aggregate_fees_for_vault() { 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 vault_factory_id = store_vault_factory_code(&mut app); let pool_factory_id = store_pool_factory_code(&mut app); let pool_router_id = store_pool_router_code(&mut app); @@ -2174,6 +2247,42 @@ fn aggregate_fees_for_vault() { ) .unwrap(); + let fee_distributor_address = app + .instantiate_contract( + fee_distributor_id, + creator.clone().sender, + &white_whale::fee_distributor::InstantiateMsg { + bonding_contract_addr: "whale_lair".clone().to_string(), + fee_collector_addr: fee_collector_address.clone().to_string(), + 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 + }, + distribution_asset: AssetInfo::NativeToken { + denom: "uatom".to_string(), + }, + }, + &[], + "fee_distributor", + None, + ) + .unwrap(); + + app.execute_contract( + creator.sender.clone(), + fee_collector_address.clone(), + &white_whale::fee_collector::ExecuteMsg::UpdateConfig { + owner: None, + pool_router: None, + fee_distributor: Some(fee_distributor_address.to_string()), + pool_factory: None, + vault_factory: None, + }, + &[], + ) + .unwrap(); + let vault_factory_address = app .instantiate_contract( vault_factory_id, @@ -2512,7 +2621,6 @@ fn aggregate_fees_for_vault() { creator.sender.clone(), fee_collector_address.clone(), &AggregateFees { - asset_info: ask_asset.clone(), aggregate_fees_for: FeesFor::Factory { factory_addr: vault_factory_address.to_string(), factory_type: FactoryType::Vault { @@ -2650,7 +2758,6 @@ fn aggregate_fees_for_vault() { creator.sender, fee_collector_address.clone(), &AggregateFees { - asset_info: ask_asset.clone(), aggregate_fees_for: FeesFor::Factory { factory_addr: vault_factory_address.to_string(), factory_type: FactoryType::Vault { @@ -5748,6 +5855,7 @@ fn aggregate_fees_unsuccessfully() { 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( @@ -5760,15 +5868,48 @@ fn aggregate_fees_unsuccessfully() { ) .unwrap(); - // try to aggregate fees from an unauthorized address + 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("unauthorized"), + Addr::unchecked("anyone"), fee_collector_address.clone(), &AggregateFees { - asset_info: AssetInfo::NativeToken { - denom: "uwhale".to_string(), - }, aggregate_fees_for: FeesFor::Contracts { contracts: vec![] }, }, &[], @@ -5777,7 +5918,7 @@ fn aggregate_fees_unsuccessfully() { assert_eq!( err.downcast::().unwrap(), - ContractError::Unauthorized {} + ContractError::InvalidContractsFeeAggregation {} ); } diff --git a/contracts/liquidity_hub/fee_collector/src/tests/testing.rs b/contracts/liquidity_hub/fee_collector/src/tests/testing.rs index 8de50dc1..e1e6f204 100644 --- a/contracts/liquidity_hub/fee_collector/src/tests/testing.rs +++ b/contracts/liquidity_hub/fee_collector/src/tests/testing.rs @@ -2,16 +2,12 @@ use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::{from_binary, Addr, DepsMut, MessageInfo, Response}; use cw2::{get_contract_version, set_contract_version, ContractVersion}; use std::env; -use white_whale::pool_network::asset::AssetInfo; use crate::contract::{execute, instantiate, migrate, query}; use white_whale::pool_network::mock_querier::mock_dependencies; use crate::ContractError; -use white_whale::fee_collector::ExecuteMsg::AggregateFees; -use white_whale::fee_collector::{ - Config, ExecuteMsg, FeesFor, InstantiateMsg, MigrateMsg, QueryMsg, -}; +use white_whale::fee_collector::{Config, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; pub fn mock_instantiation(deps: DepsMut, info: MessageInfo) -> Result { let msg = InstantiateMsg {}; @@ -32,27 +28,6 @@ fn proper_initialization() { assert_eq!("owner".to_string(), config_res.owner); } -#[test] -fn collect_fees_unsuccessfully_unauthorized() { - let mut deps = mock_dependencies(&[]); - let info = mock_info("owner", &[]); - mock_instantiation(deps.as_mut(), info).unwrap(); - - // unauthorized tries collecting fees - let info = mock_info("unauthorized", &[]); - let msg = ExecuteMsg::CollectFees { - collect_fees_for: FeesFor::Contracts { contracts: vec![] }, - }; - - let res = execute(deps.as_mut(), mock_env(), info, msg); - - match res { - Ok(_) => panic!("should return ContractError::Unauthorized"), - Err(ContractError::Unauthorized {}) => (), - _ => panic!("should return ContractError::Unauthorized"), - } -} - #[test] fn test_update_config_successfully() { let mut deps = mock_dependencies(&[]); @@ -149,26 +124,3 @@ fn test_migration() { _ => panic!("should return ContractError::Std"), } } - -#[test] -fn test_aggregate_fee_for_contracts_err() { - let mut deps = mock_dependencies(&[]); - let info = mock_info("owner", &[]); - mock_instantiation(deps.as_mut(), info.clone()).unwrap(); - - // should error, can't collect fees for contracts - let msg = AggregateFees { - asset_info: AssetInfo::NativeToken { - denom: "uluna".to_string(), - }, - aggregate_fees_for: FeesFor::Contracts { contracts: vec![] }, - }; - - let res = execute(deps.as_mut(), mock_env(), info, msg); - - match res { - Ok(_) => panic!("should return ContractError::InvalidContractsFeeAggregation"), - Err(ContractError::InvalidContractsFeeAggregation {}) => (), - _ => panic!("should return ContractError::InvalidContractsFeeAggregation"), - } -} diff --git a/packages/white-whale/src/fee_collector.rs b/packages/white-whale/src/fee_collector.rs index 57a80ccb..d4c68196 100644 --- a/packages/white-whale/src/fee_collector.rs +++ b/packages/white-whale/src/fee_collector.rs @@ -10,12 +10,9 @@ pub struct InstantiateMsg {} pub enum ExecuteMsg { /// Collects protocol fees based on the configuration indicated by [FeesFor] CollectFees { collect_fees_for: FeesFor }, - /// Swaps the assets (fees) sitting in the fee collector into the given [AssetInfo] if possible. - /// A [SwapRoute] should be available at the router to be able to make the swaps. - AggregateFees { - asset_info: AssetInfo, - aggregate_fees_for: FeesFor, - }, + /// Swaps the assets (fees) sitting in the fee collector into the distribution asset set by the + /// fee collector. A [SwapRoute] should be available at the router to be able to make the swaps. + AggregateFees { aggregate_fees_for: FeesFor }, /// Forward fees to the fee distributor. This will collect and aggregate the fees, to send them back to the fee distributor. ForwardFees { epoch: Epoch,