komple_framework_fee_module/
contract.rs

1use std::ops::Mul;
2
3#[cfg(not(feature = "library"))]
4use cosmwasm_std::entry_point;
5use cosmwasm_std::{
6    coin, from_binary, to_binary, Attribute, BankMsg, Binary, CosmosMsg, Decimal, Deps, DepsMut,
7    Env, MessageInfo, Order, Response, StdResult, Uint128, WasmMsg,
8};
9use cw2::set_contract_version;
10use cw20::{Cw20ExecuteMsg, Cw20ReceiveMsg};
11use cw_storage_plus::Bound;
12use komple_framework_types::modules::fee::{Fees, FixedPayment, PercentagePayment};
13use komple_framework_types::modules::Modules;
14use komple_framework_types::shared::query::ResponseWrapper;
15use komple_framework_types::shared::RegisterMsg;
16use komple_framework_utils::check_admin_privileges;
17use komple_framework_utils::funds::{check_single_amount, FundsError};
18use komple_framework_utils::response::{EventHelper, ResponseHelper};
19use komple_framework_utils::shared::{execute_lock_execute, execute_update_operators};
20
21use crate::error::ContractError;
22use crate::msg::{
23    CustomPaymentAddress, ExecuteMsg, FixedFeeResponse, PercentageFeeResponse, QueryMsg, ReceiveMsg,
24};
25use crate::state::{
26    Config, CONFIG, EXECUTE_LOCK, FIXED_FEES, HUB_ADDR, OPERATORS, PERCENTAGE_FEES,
27};
28
29// version info for migration info
30const CONTRACT_NAME: &str = "crates.io:komple-framework-fee-module";
31const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
32
33#[cfg_attr(not(feature = "library"), entry_point)]
34pub fn instantiate(
35    deps: DepsMut,
36    _env: Env,
37    info: MessageInfo,
38    msg: RegisterMsg,
39) -> Result<Response, ContractError> {
40    set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
41
42    let admin = deps.api.addr_validate(&msg.admin)?;
43
44    let config = Config { admin };
45    CONFIG.save(deps.storage, &config)?;
46
47    HUB_ADDR.save(deps.storage, &info.sender)?;
48
49    EXECUTE_LOCK.save(deps.storage, &false)?;
50
51    Ok(ResponseHelper::new_module("fee", "instantiate").add_event(
52        EventHelper::new("fee_instantiate")
53            .add_attribute("admin", config.admin.to_string())
54            .add_attribute("hub_addr", info.sender)
55            .get(),
56    ))
57}
58
59#[cfg_attr(not(feature = "library"), entry_point)]
60pub fn execute(
61    deps: DepsMut,
62    env: Env,
63    info: MessageInfo,
64    msg: ExecuteMsg,
65) -> Result<Response, ContractError> {
66    let execute_lock = EXECUTE_LOCK.load(deps.storage)?;
67    if execute_lock {
68        return Err(ContractError::ExecuteLocked {});
69    };
70
71    match msg {
72        ExecuteMsg::SetFee {
73            fee_type,
74            module_name,
75            fee_name,
76            data,
77        } => execute_set_fee(deps, env, info, fee_type, module_name, fee_name, data),
78        ExecuteMsg::RemoveFee {
79            fee_type,
80            module_name,
81            fee_name,
82        } => execute_remove_fee(deps, env, info, fee_type, module_name, fee_name),
83        ExecuteMsg::Distribute {
84            fee_type,
85            module_name,
86            custom_payment_addresses,
87        } => execute_distribute(
88            deps,
89            env,
90            info,
91            fee_type,
92            module_name,
93            custom_payment_addresses,
94            None,
95        ),
96        ExecuteMsg::UpdateOperators { addrs } => {
97            let config = CONFIG.load(deps.storage)?;
98            let res = execute_update_operators(
99                deps,
100                info,
101                Modules::Fee.as_str(),
102                &env.contract.address,
103                &config.admin,
104                OPERATORS,
105                addrs,
106            );
107            match res {
108                Ok(res) => Ok(res),
109                Err(err) => Err(err.into()),
110            }
111        }
112        ExecuteMsg::LockExecute {} => {
113            let res = execute_lock_execute(
114                deps,
115                info,
116                Modules::Fee.as_str(),
117                &env.contract.address,
118                EXECUTE_LOCK,
119            );
120            match res {
121                Ok(res) => Ok(res),
122                Err(e) => Err(e.into()),
123            }
124        }
125        ExecuteMsg::Receive(msg) => execute_receive(deps, env, info, msg),
126    }
127}
128
129fn execute_set_fee(
130    deps: DepsMut,
131    env: Env,
132    info: MessageInfo,
133    fee_type: Fees,
134    module_name: String,
135    fee_name: String,
136    data: Binary,
137) -> Result<Response, ContractError> {
138    let hub_addr = HUB_ADDR.may_load(deps.storage)?;
139    let config = CONFIG.load(deps.storage)?;
140    let operators = OPERATORS.may_load(deps.storage)?;
141    check_admin_privileges(
142        &info.sender,
143        &env.contract.address,
144        &config.admin,
145        hub_addr,
146        operators,
147    )?;
148
149    let mut event_attributes: Vec<Attribute> = vec![];
150
151    match fee_type {
152        Fees::Fixed => {
153            let fixed_payment: FixedPayment = from_binary(&data)?;
154            if fixed_payment.value.is_zero() {
155                return Err(ContractError::InvalidFee {});
156            };
157
158            if fixed_payment.address.is_some() {
159                deps.api
160                    .addr_validate(&fixed_payment.address.clone().unwrap())?;
161            }
162
163            FIXED_FEES.save(deps.storage, (&module_name, &fee_name), &fixed_payment)?;
164
165            event_attributes.push(Attribute {
166                key: "value".to_string(),
167                value: fixed_payment.value.to_string(),
168            });
169            if let Some(payment_address) = fixed_payment.address {
170                event_attributes.push(Attribute {
171                    key: "address".to_string(),
172                    value: payment_address,
173                });
174            }
175        }
176        Fees::Percentage => {
177            let percentage_payment: PercentagePayment = from_binary(&data)?;
178            if percentage_payment.value > Decimal::one() {
179                return Err(ContractError::InvalidFee {});
180            };
181
182            // Query total fee percentage for a given module
183            // Total fee decimal cannot be equal or higher than 1
184            // If we have an existing state, we need to subtract the current value before additions
185            let current_percentage_payment =
186                PERCENTAGE_FEES.may_load(deps.storage, (&module_name, &fee_name))?;
187            let current_percentage_payment_value = match current_percentage_payment {
188                Some(p) => p.value,
189                None => Decimal::zero(),
190            };
191            let total_fee =
192                query_total_percentage_fees(deps.as_ref(), module_name.clone(), None, None)?;
193            if total_fee.data - current_percentage_payment_value + percentage_payment.value
194                >= Decimal::one()
195            {
196                return Err(ContractError::InvalidTotalFee {});
197            };
198
199            if percentage_payment.address.is_some() {
200                deps.api
201                    .addr_validate(&percentage_payment.address.clone().unwrap())?;
202            };
203
204            PERCENTAGE_FEES.save(deps.storage, (&module_name, &fee_name), &percentage_payment)?;
205
206            event_attributes.push(Attribute {
207                key: "value".to_string(),
208                value: percentage_payment.value.to_string(),
209            });
210            if let Some(payment_address) = percentage_payment.address {
211                event_attributes.push(Attribute {
212                    key: "address".to_string(),
213                    value: payment_address,
214                });
215            }
216        }
217    }
218
219    Ok(ResponseHelper::new_module("fee", "set_fee").add_event(
220        EventHelper::new("fee_set_fee")
221            .add_attribute("fee_type", fee_type.as_str())
222            .add_attribute("module_name", &module_name)
223            .add_attribute("fee_name", &fee_name)
224            .add_attributes(event_attributes)
225            .get(),
226    ))
227}
228
229fn execute_remove_fee(
230    deps: DepsMut,
231    env: Env,
232    info: MessageInfo,
233    fee_type: Fees,
234    module_name: String,
235    fee_name: String,
236) -> Result<Response, ContractError> {
237    let hub_addr = HUB_ADDR.may_load(deps.storage)?;
238    let config = CONFIG.load(deps.storage)?;
239    let operators = OPERATORS.may_load(deps.storage)?;
240    check_admin_privileges(
241        &info.sender,
242        &env.contract.address,
243        &config.admin,
244        hub_addr,
245        operators,
246    )?;
247
248    match fee_type {
249        Fees::Fixed => FIXED_FEES.remove(deps.storage, (&module_name, &fee_name)),
250        Fees::Percentage => PERCENTAGE_FEES.remove(deps.storage, (&module_name, &fee_name)),
251    }
252
253    Ok(ResponseHelper::new_module("fee", "remove_fee").add_event(
254        EventHelper::new("fee_remove_fee")
255            .add_attribute("fee_type", fee_type.as_str())
256            .add_attribute("module_name", &module_name)
257            .add_attribute("fee_name", &fee_name)
258            .get(),
259    ))
260}
261
262fn execute_distribute(
263    deps: DepsMut,
264    _env: Env,
265    info: MessageInfo,
266    fee_type: Fees,
267    module_name: String,
268    custom_payment_addresses: Option<Vec<CustomPaymentAddress>>,
269    cw20_token_amount: Option<Uint128>,
270) -> Result<Response, ContractError> {
271    let msgs = match fee_type {
272        Fees::Fixed => _distribute_fixed_fee(
273            deps,
274            info,
275            &module_name,
276            custom_payment_addresses,
277            cw20_token_amount,
278        )?,
279        Fees::Percentage => _distribute_percentage_fee(
280            deps,
281            info,
282            &module_name,
283            custom_payment_addresses,
284            cw20_token_amount,
285        )?,
286    };
287
288    Ok(ResponseHelper::new_module("fee", "distribute")
289        .add_messages(msgs)
290        .add_event(
291            EventHelper::new("fee_distribute")
292                .add_attribute("fee_type", fee_type.as_str())
293                .add_attribute("module_name", &module_name)
294                .get(),
295        ))
296}
297
298fn _distribute_fixed_fee(
299    deps: DepsMut,
300    info: MessageInfo,
301    module_name: &str,
302    custom_payment_addresses: Option<Vec<CustomPaymentAddress>>,
303    cw20_token_amount: Option<Uint128>,
304) -> Result<Vec<CosmosMsg>, ContractError> {
305    let mut msgs: Vec<CosmosMsg> = vec![];
306
307    // All of the available amounts to distribute fee
308    let amounts = FIXED_FEES
309        .prefix(&module_name)
310        .range(deps.storage, None, None, Order::Ascending)
311        .map(|item| {
312            let (fee_name, fixed_payment) = item.unwrap();
313            FixedFeeResponse {
314                module_name: module_name.to_string(),
315                fee_name,
316                address: fixed_payment.address,
317                value: fixed_payment.value,
318            }
319        })
320        .collect::<Vec<FixedFeeResponse>>();
321
322    if amounts.is_empty() {
323        return Err(ContractError::NoPaymentsFound {});
324    }
325
326    // Total amount
327    let total_amount = amounts.iter().map(|item| item.value).sum::<Uint128>();
328    if cw20_token_amount.is_none() {
329        check_single_amount(&info, total_amount)?;
330    } else {
331        if cw20_token_amount.unwrap() != total_amount {
332            return Err(FundsError::InvalidFunds {
333                got: cw20_token_amount.unwrap().to_string(),
334                expected: total_amount.to_string(),
335            }
336            .into());
337        };
338    }
339
340    // Make bank send messages based on fee percentage
341    for amount in amounts {
342        let mut is_custom_address = false;
343
344        // If we have some custom addresses find and replace the address
345        if let Some(custom_payment_addresses) = custom_payment_addresses.clone() {
346            // Find the correct custom address based on share name
347            let custom_payment_address = custom_payment_addresses
348                .iter()
349                .find(|item| amount.fee_name == item.fee_name);
350            if let Some(custom_payment_address) = custom_payment_address {
351                is_custom_address = true;
352                let msg = match cw20_token_amount.is_none() {
353                    true => CosmosMsg::Bank(BankMsg::Send {
354                        to_address: custom_payment_address.address.to_string(),
355                        amount: vec![coin(amount.value.u128(), info.funds[0].denom.clone())],
356                    }),
357                    false => CosmosMsg::Wasm(WasmMsg::Execute {
358                        contract_addr: info.sender.to_string(),
359                        msg: to_binary(&Cw20ExecuteMsg::Transfer {
360                            recipient: custom_payment_address.address.to_string(),
361                            amount: amount.value,
362                        })?,
363                        funds: vec![],
364                    }),
365                };
366                msgs.push(msg);
367            }
368        }
369
370        // Carry on without replacing the addresses
371        if !is_custom_address {
372            if let Some(payment_address) = amount.address {
373                let msg = match cw20_token_amount.is_none() {
374                    true => CosmosMsg::Bank(BankMsg::Send {
375                        to_address: payment_address.to_string(),
376                        amount: vec![coin(amount.value.u128(), info.funds[0].denom.clone())],
377                    }),
378                    false => CosmosMsg::Wasm(WasmMsg::Execute {
379                        contract_addr: info.sender.to_string(),
380                        msg: to_binary(&Cw20ExecuteMsg::Transfer {
381                            recipient: payment_address.to_string(),
382                            amount: amount.value,
383                        })?,
384                        funds: vec![],
385                    }),
386                };
387                msgs.push(msg);
388            };
389        }
390    }
391
392    Ok(msgs)
393}
394
395fn _distribute_percentage_fee(
396    deps: DepsMut,
397    info: MessageInfo,
398    module_name: &str,
399    custom_payment_addresses: Option<Vec<CustomPaymentAddress>>,
400    cw20_token_amount: Option<Uint128>,
401) -> Result<Vec<CosmosMsg>, ContractError> {
402    let mut msgs: Vec<CosmosMsg> = vec![];
403
404    // All of the available percentages to distribute fee
405    let percentages = PERCENTAGE_FEES
406        .prefix(&module_name)
407        .range(deps.storage, None, None, Order::Ascending)
408        .map(|item| {
409            let (fee_name, percentage_payment) = item.unwrap();
410            PercentageFeeResponse {
411                module_name: module_name.to_string(),
412                fee_name,
413                address: percentage_payment.address,
414                value: percentage_payment.value,
415            }
416        })
417        .collect::<Vec<PercentageFeeResponse>>();
418
419    if percentages.is_empty() {
420        return Err(ContractError::NoPaymentsFound {});
421    }
422
423    // Total amount of fee percentage
424    let total_fee = percentages
425        .iter()
426        .map(|item| item.value)
427        .sum::<Decimal>()
428        .mul(Uint128::new(100));
429
430    // Make bank send messages based on fee percentage
431    for percentage in percentages {
432        let mut is_custom_address = false;
433
434        // Payment amount is total_funds * percentage / total_fee
435        let payment_amount = match cw20_token_amount {
436            Some(amount) => amount
437                .mul(percentage.value.mul(Uint128::new(100)))
438                .checked_div(total_fee)?,
439            None => info.funds[0]
440                .amount
441                .mul(percentage.value.mul(Uint128::new(100)))
442                .checked_div(total_fee)?,
443        };
444
445        // If we have some custom addresses find and replace the address
446        if let Some(custom_payment_addresses) = custom_payment_addresses.clone() {
447            // Find the correct custom address based on share name
448            let custom_payment_address = custom_payment_addresses
449                .iter()
450                .find(|item| percentage.fee_name == item.fee_name);
451            if let Some(custom_payment_address) = custom_payment_address {
452                is_custom_address = true;
453                let msg = match cw20_token_amount.is_none() {
454                    true => CosmosMsg::Bank(BankMsg::Send {
455                        to_address: custom_payment_address.address.to_string(),
456                        amount: vec![coin(payment_amount.u128(), info.funds[0].denom.clone())],
457                    }),
458                    false => CosmosMsg::Wasm(WasmMsg::Execute {
459                        contract_addr: info.sender.to_string(),
460                        msg: to_binary(&Cw20ExecuteMsg::Transfer {
461                            recipient: custom_payment_address.address.to_string(),
462                            amount: payment_amount,
463                        })?,
464                        funds: vec![],
465                    }),
466                };
467                msgs.push(msg);
468            }
469        }
470
471        // Carry on without replacing the addresses
472        if !is_custom_address {
473            if let Some(payment_address) = percentage.address {
474                let msg = match cw20_token_amount.is_none() {
475                    true => CosmosMsg::Bank(BankMsg::Send {
476                        to_address: payment_address.to_string(),
477                        amount: vec![coin(payment_amount.u128(), info.funds[0].denom.clone())],
478                    }),
479                    false => CosmosMsg::Wasm(WasmMsg::Execute {
480                        contract_addr: info.sender.to_string(),
481                        msg: to_binary(&Cw20ExecuteMsg::Transfer {
482                            recipient: payment_address.to_string(),
483                            amount: payment_amount,
484                        })?,
485                        funds: vec![],
486                    }),
487                };
488                msgs.push(msg);
489            };
490        }
491    }
492
493    Ok(msgs)
494}
495
496fn execute_receive(
497    deps: DepsMut,
498    env: Env,
499    info: MessageInfo,
500    cw20_receive_msg: Cw20ReceiveMsg,
501) -> Result<Response, ContractError> {
502    let msg: ReceiveMsg = from_binary(&cw20_receive_msg.msg)?;
503    let amount = cw20_receive_msg.amount;
504
505    match msg {
506        ReceiveMsg::Distribute {
507            fee_type,
508            module_name,
509            custom_payment_addresses,
510        } => execute_distribute(
511            deps,
512            env,
513            info,
514            fee_type,
515            module_name,
516            custom_payment_addresses,
517            Some(amount),
518        ),
519    }
520}
521
522#[cfg_attr(not(feature = "library"), entry_point)]
523pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
524    match msg {
525        QueryMsg::Config {} => to_binary(&query_config(deps)?),
526        QueryMsg::PercentageFee {
527            module_name,
528            fee_name,
529        } => to_binary(&query_percentage_fee(deps, module_name, fee_name)?),
530        QueryMsg::FixedFee {
531            module_name,
532            fee_name,
533        } => to_binary(&query_fixed_fee(deps, module_name, fee_name)?),
534        QueryMsg::PercentageFees {
535            module_name,
536            start_after,
537            limit,
538        } => to_binary(&query_percentage_fees(
539            deps,
540            module_name,
541            start_after,
542            limit,
543        )?),
544        QueryMsg::FixedFees {
545            module_name,
546            start_after,
547            limit,
548        } => to_binary(&query_fixed_fees(deps, module_name, start_after, limit)?),
549        QueryMsg::TotalPercentageFees {
550            module_name,
551            start_after,
552            limit,
553        } => to_binary(&query_total_percentage_fees(
554            deps,
555            module_name,
556            start_after,
557            limit,
558        )?),
559        QueryMsg::TotalFixedFees {
560            module_name,
561            start_after,
562            limit,
563        } => to_binary(&query_total_fixed_fees(
564            deps,
565            module_name,
566            start_after,
567            limit,
568        )?),
569        QueryMsg::Keys {
570            fee_type,
571            start_after,
572            limit,
573        } => to_binary(&query_keys(deps, fee_type, start_after, limit)?),
574        QueryMsg::Operators {} => to_binary(&query_operators(deps)?),
575    }
576}
577
578fn query_config(deps: Deps) -> StdResult<ResponseWrapper<Config>> {
579    let config = CONFIG.load(deps.storage)?;
580    Ok(ResponseWrapper {
581        query: "config".to_string(),
582        data: config,
583    })
584}
585
586fn query_percentage_fee(
587    deps: Deps,
588    module_name: String,
589    fee_name: String,
590) -> StdResult<ResponseWrapper<PercentageFeeResponse>> {
591    let percentage_fee = PERCENTAGE_FEES.load(deps.storage, (&module_name, &fee_name))?;
592    Ok(ResponseWrapper {
593        query: "percentage_fee".to_string(),
594        data: PercentageFeeResponse {
595            module_name,
596            fee_name,
597            address: percentage_fee.address,
598            value: percentage_fee.value,
599        },
600    })
601}
602
603fn query_fixed_fee(
604    deps: Deps,
605    module_name: String,
606    fee_name: String,
607) -> StdResult<ResponseWrapper<FixedFeeResponse>> {
608    let fixed_fee = FIXED_FEES.load(deps.storage, (&module_name, &fee_name))?;
609    Ok(ResponseWrapper {
610        query: "fixed_fee".to_string(),
611        data: FixedFeeResponse {
612            module_name,
613            fee_name,
614            address: fixed_fee.address,
615            value: fixed_fee.value,
616        },
617    })
618}
619
620fn query_percentage_fees(
621    deps: Deps,
622    module_name: String,
623    start_after: Option<String>,
624    limit: Option<u32>,
625) -> StdResult<ResponseWrapper<Vec<PercentageFeeResponse>>> {
626    let limit = limit.unwrap_or(30) as usize;
627    let start = start_after.map(|s| Bound::ExclusiveRaw(s.into()));
628
629    let percentage_fees = PERCENTAGE_FEES
630        .prefix(&module_name)
631        .range(deps.storage, start, None, Order::Ascending)
632        .take(limit)
633        .map(|item| {
634            let (fee_name, percentage_payment) = item.unwrap();
635            PercentageFeeResponse {
636                module_name: module_name.clone(),
637                fee_name,
638                address: percentage_payment.address,
639                value: percentage_payment.value,
640            }
641        })
642        .collect::<Vec<PercentageFeeResponse>>();
643
644    Ok(ResponseWrapper {
645        query: "percentage_fees".to_string(),
646        data: percentage_fees,
647    })
648}
649
650fn query_fixed_fees(
651    deps: Deps,
652    module_name: String,
653    start_after: Option<String>,
654    limit: Option<u32>,
655) -> StdResult<ResponseWrapper<Vec<FixedFeeResponse>>> {
656    let limit = limit.unwrap_or(30) as usize;
657    let start = start_after.map(|s| Bound::ExclusiveRaw(s.into()));
658
659    let fixed_fees = FIXED_FEES
660        .prefix(&module_name)
661        .range(deps.storage, start, None, Order::Ascending)
662        .take(limit)
663        .map(|item| {
664            let (fee_name, fixed_payment) = item.unwrap();
665            FixedFeeResponse {
666                module_name: module_name.clone(),
667                fee_name,
668                address: fixed_payment.address,
669                value: fixed_payment.value,
670            }
671        })
672        .collect::<Vec<FixedFeeResponse>>();
673
674    Ok(ResponseWrapper {
675        query: "fixed_fees".to_string(),
676        data: fixed_fees,
677    })
678}
679
680fn query_total_percentage_fees(
681    deps: Deps,
682    module_name: String,
683    start_after: Option<String>,
684    limit: Option<u32>,
685) -> StdResult<ResponseWrapper<Decimal>> {
686    let limit = limit.unwrap_or(30) as usize;
687    let start = start_after.map(|s| Bound::ExclusiveRaw(s.into()));
688
689    let total_percentage = PERCENTAGE_FEES
690        .prefix(&module_name)
691        .range(deps.storage, start, None, Order::Ascending)
692        .take(limit)
693        .map(|item| {
694            let (_, percentage_payment) = item.unwrap();
695            percentage_payment.value
696        })
697        .sum::<Decimal>();
698
699    Ok(ResponseWrapper {
700        query: "total_percentage_fees".to_string(),
701        data: total_percentage,
702    })
703}
704
705fn query_total_fixed_fees(
706    deps: Deps,
707    module_name: String,
708    start_after: Option<String>,
709    limit: Option<u32>,
710) -> StdResult<ResponseWrapper<Uint128>> {
711    let limit = limit.unwrap_or(30) as usize;
712    let start = start_after.map(|s| Bound::ExclusiveRaw(s.into()));
713
714    let total_fixed = FIXED_FEES
715        .prefix(&module_name)
716        .range(deps.storage, start, None, Order::Ascending)
717        .take(limit)
718        .map(|item| {
719            let (_, fixed_payment) = item.unwrap();
720            fixed_payment.value
721        })
722        .sum::<Uint128>();
723
724    Ok(ResponseWrapper {
725        query: "total_fixed_fees".to_string(),
726        data: total_fixed,
727    })
728}
729
730// TODO: Fix this query
731// Need to add pagination
732fn query_keys(
733    deps: Deps,
734    fee_type: Fees,
735    _start_after: Option<String>,
736    limit: Option<u32>,
737) -> StdResult<ResponseWrapper<Vec<String>>> {
738    let limit = limit.unwrap_or(30) as usize;
739    // let start = start_after.map(|s| Bound::ExclusiveRaw(s.into()));
740
741    let modules = match fee_type {
742        Fees::Fixed => FIXED_FEES
743            .keys(deps.storage, None, None, Order::Descending)
744            .take(limit)
745            .map(|item| {
746                let (module_name, _) = item.unwrap();
747                module_name
748            })
749            .collect::<Vec<String>>(),
750        Fees::Percentage => PERCENTAGE_FEES
751            .keys(deps.storage, None, None, Order::Descending)
752            .take(limit)
753            .map(|item| {
754                let (module_name, _) = item.unwrap();
755                module_name
756            })
757            .collect::<Vec<String>>(),
758    };
759
760    Ok(ResponseWrapper {
761        query: "modules".to_string(),
762        data: modules,
763    })
764}
765
766fn query_operators(deps: Deps) -> StdResult<ResponseWrapper<Vec<String>>> {
767    let addrs = OPERATORS.may_load(deps.storage)?;
768    let addrs = match addrs {
769        Some(addrs) => addrs.iter().map(|a| a.to_string()).collect(),
770        None => vec![],
771    };
772    Ok(ResponseWrapper::new("operators", addrs))
773}