1 import { createSlice } from '@reduxjs/toolkit';
3 import { type ModelState, getInitialModelState } from '@proton/account';
4 import { WasmBlockchainClient } from '@proton/andromeda';
5 import { createAsyncModelThunk, handleAsyncModel, previousSelector } from '@proton/redux-utilities';
7 import type { WalletThunkArguments } from '../thunk';
9 const name = 'network_fees' as const;
11 type BlockTarget = number;
12 type FeeRate = number;
14 export type FeeRateByBlockTarget = [BlockTarget, FeeRate];
16 export interface FeeSettings {
17 feesMap: Map<string, number>;
18 feesList: FeeRateByBlockTarget[];
19 minimumBroadcastFee: number;
20 minimumIncrementalFee: number;
23 export interface NetworkFeesState {
24 [name]: ModelState<FeeSettings>;
27 type SliceState = NetworkFeesState[typeof name];
28 type Model = NonNullable<SliceState['value']>;
30 export const selectNetworkFees = (state: NetworkFeesState) => state[name];
32 export const DEFAULT_FEE_SETTINGS = {
35 minimumBroadcastFee: 1.0,
36 minimumIncrementalFee: 1.0,
38 const initialState = getInitialModelState<Model>(DEFAULT_FEE_SETTINGS);
40 const feesMapToList = (feesMap: Map<string, number>) => {
42 [...feesMap.entries()]
43 // We need to round feeRate because bdk expects a BigInt
44 .map(([block, feeRate]): FeeRateByBlockTarget => [Number(block), Math.round(feeRate)])
45 .filter(([block]) => Number.isFinite(block))
46 .sort(([a], [b]) => a - b)
50 const modelThunk = createAsyncModelThunk<Model, NetworkFeesState, WalletThunkArguments>(`${name}/fetch`, {
51 miss: async ({ extraArgument }) => {
52 const isMinFeeEnabled = await extraArgument.unleashClient.isEnabled('WalletMinFee');
54 const blockchainClient = new WasmBlockchainClient(extraArgument.walletApi);
56 const feesMap = await blockchainClient.getFeesEstimation();
57 const { MinimumBroadcastFee: minimumBroadcastFee, MinimumIncrementalFee: minimumIncrementalFee } =
59 ? await blockchainClient.getMininumFees()
60 : { MinimumBroadcastFee: 1.0, MinimumIncrementalFee: 1.0 };
62 const feesList = feesMapToList(feesMap);
64 return { feesMap, feesList, minimumBroadcastFee, minimumIncrementalFee };
66 previous: previousSelector(selectNetworkFees),
69 const slice = createSlice({
73 extraReducers: (builder) => {
74 handleAsyncModel(builder, modelThunk);
78 export const networkFeesReducer = { [name]: slice.reducer };
79 export const networkFeesThunk = modelThunk.thunk;