Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

Commit 457fd0d

Browse files
fix: resolve comments
1 parent 1ecde55 commit 457fd0d

3 files changed

Lines changed: 179 additions & 128 deletions

File tree

src/ui/common/services/bsnService.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,19 @@ export const BSN_CONFIGS: Record<string, BsnConfig> = {
3232
COSMOS: {
3333
modalTitle: "Select Cosmos Finality Provider",
3434
filterOptions: [
35-
{ value: "active", label: "Active" },
36-
{ value: "inactive", label: "Inactive" },
35+
{ value: "registered", label: "Registered" },
36+
{ value: "slashed", label: "Slashed" },
3737
],
3838
fpFilterBehavior: "status-based",
3939
},
4040
// Roll-up BSN config
4141
ROLLUP: {
4242
modalTitle: "Select Roll Up Finality Provider",
4343
filterOptions: [
44-
{ value: "active", label: "Allowlisted" },
45-
{ value: "inactive", label: "Not Allowlisted" },
44+
{ value: "allowlisted", label: "Allow Listed" },
45+
{ value: "non-allowlisted", label: "Non-Allow Listed" },
46+
{ value: "slashed", label: "Slashed" },
47+
{ value: "jailed", label: "Jailed" },
4648
],
4749
fpFilterBehavior: "allowlist-based",
4850
},
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import { getNetworkConfigBBN } from "@/ui/common/config/network/bbn";
2+
import { Bsn } from "@/ui/common/types/bsn";
3+
import {
4+
FinalityProviderState as FinalityProviderStateEnum,
5+
type FinalityProvider,
6+
} from "@/ui/common/types/finalityProviders";
7+
8+
const { chainId: BBN_CHAIN_ID } = getNetworkConfigBBN();
9+
10+
/**
11+
* Normalizes hex string by removing 0x prefix and converting to lowercase
12+
*/
13+
export const normalizeHex = (hex?: string): string =>
14+
(hex ?? "").trim().toLowerCase().replace(/^0x/, "");
15+
16+
// BSN-specific status filters to handle different BSN behaviors
17+
const BABYLON_STATUS_FILTERS = {
18+
active: (fp: FinalityProvider) =>
19+
fp.state === FinalityProviderStateEnum.ACTIVE,
20+
inactive: (fp: FinalityProvider) =>
21+
fp.state === FinalityProviderStateEnum.INACTIVE,
22+
jailed: (fp: FinalityProvider) =>
23+
fp.state === FinalityProviderStateEnum.JAILED,
24+
slashed: (fp: FinalityProvider) =>
25+
fp.state === FinalityProviderStateEnum.SLASHED,
26+
};
27+
28+
const COSMOS_STATUS_FILTERS = {
29+
registered: (fp: FinalityProvider) =>
30+
fp.state === FinalityProviderStateEnum.INACTIVE,
31+
slashed: (fp: FinalityProvider) =>
32+
fp.state === FinalityProviderStateEnum.SLASHED,
33+
};
34+
35+
const BSN_STATUS_FILTERS = {
36+
[BBN_CHAIN_ID]: BABYLON_STATUS_FILTERS,
37+
COSMOS: COSMOS_STATUS_FILTERS,
38+
};
39+
40+
/**
41+
* Creates allowlist filters for BSN-based filtering
42+
*/
43+
export const createAllowlistFilters = (selectedBsn: Bsn | undefined) => {
44+
const allowSet = new Set((selectedBsn?.allowlist || []).map(normalizeHex));
45+
46+
return {
47+
allowlisted: (fp: FinalityProvider) => allowSet.has(normalizeHex(fp.btcPk)),
48+
"non-allowlisted": (fp: FinalityProvider) =>
49+
!allowSet.has(normalizeHex(fp.btcPk)),
50+
};
51+
};
52+
53+
/**
54+
* Applies standard status filtering for BABYLON and COSMOS BSNs
55+
*/
56+
export const applyStandardStatusFilter = (
57+
providers: FinalityProvider[],
58+
filterValue: string,
59+
bsnKey: string,
60+
): FinalityProvider[] => {
61+
const bsnStatusFilters =
62+
BSN_STATUS_FILTERS[bsnKey] || BSN_STATUS_FILTERS[BBN_CHAIN_ID];
63+
const statusFilter =
64+
bsnStatusFilters[filterValue as keyof typeof bsnStatusFilters];
65+
return statusFilter ? providers.filter(statusFilter) : providers;
66+
};
67+
68+
export interface FinalityProviderFilterState {
69+
searchTerm: string;
70+
providerStatus:
71+
| "active"
72+
| "inactive"
73+
| "registered"
74+
| "allowlisted"
75+
| "non-allowlisted"
76+
| "slashed"
77+
| "jailed"
78+
| "";
79+
allowlistStatus: "allowlisted" | "non-allowlisted" | "";
80+
}
81+
82+
/**
83+
* Main filtering function that applies BSN-aware filtering logic
84+
* Handles both allowlist-based and status-based filtering behaviors
85+
*/
86+
export const filterFinalityProvidersByBsn = (
87+
providers: FinalityProvider[],
88+
filter: FinalityProviderFilterState,
89+
selectedBsn: Bsn | undefined,
90+
fpFilterBehavior: "status-based" | "allowlist-based",
91+
): FinalityProvider[] => {
92+
let filtered = providers;
93+
94+
// Apply BSN-aware filtering based on provider status
95+
if (filter.providerStatus) {
96+
if (fpFilterBehavior === "allowlist-based") {
97+
// For rollup BSNs: filter FPs by allowlist status or specific states
98+
const allowSet = new Set(
99+
(selectedBsn?.allowlist || []).map(normalizeHex),
100+
);
101+
102+
filtered = filtered.filter((fp) => {
103+
const isAllowlisted = allowSet.has(normalizeHex(fp.btcPk));
104+
105+
if (filter.providerStatus === "allowlisted") {
106+
return isAllowlisted;
107+
} else if (filter.providerStatus === "non-allowlisted") {
108+
return !isAllowlisted;
109+
} else if (filter.providerStatus === "slashed") {
110+
return fp.state === FinalityProviderStateEnum.SLASHED;
111+
} else if (filter.providerStatus === "jailed") {
112+
return fp.state === FinalityProviderStateEnum.JAILED;
113+
}
114+
return true;
115+
});
116+
} else if (fpFilterBehavior === "status-based") {
117+
// For status-based BSNs: filter by finality provider state using BSN-specific filters
118+
const bsnKey =
119+
selectedBsn?.id === BBN_CHAIN_ID
120+
? BBN_CHAIN_ID
121+
: selectedBsn?.type || BBN_CHAIN_ID;
122+
123+
filtered = applyStandardStatusFilter(
124+
filtered,
125+
filter.providerStatus,
126+
bsnKey,
127+
);
128+
}
129+
}
130+
131+
if (filter.allowlistStatus) {
132+
const allowlistFilters = createAllowlistFilters(selectedBsn);
133+
const allowlistFilter =
134+
allowlistFilters[filter.allowlistStatus as keyof typeof allowlistFilters];
135+
if (allowlistFilter) {
136+
filtered = filtered.filter(allowlistFilter);
137+
}
138+
}
139+
140+
return filtered;
141+
};
142+
143+
/**
144+
* Determines if a finality provider row should be selectable based on BSN rules
145+
*/
146+
export const isFinalityProviderRowSelectable = (
147+
row: FinalityProvider,
148+
selectedBsnId: string | undefined,
149+
selectedBsn: Bsn | undefined,
150+
): boolean => {
151+
const statusAllowed =
152+
row.state === FinalityProviderStateEnum.ACTIVE ||
153+
row.state === FinalityProviderStateEnum.INACTIVE;
154+
155+
// For selection (not filtering), only restrict based on allowlist for non-Babylon BSNs
156+
const allowlistAllowed =
157+
!selectedBsnId ||
158+
selectedBsnId === BBN_CHAIN_ID ||
159+
(selectedBsn?.allowlist || []).some(
160+
(allowedPk) => normalizeHex(allowedPk) === normalizeHex(row.btcPk),
161+
);
162+
163+
return statusAllowed && allowlistAllowed;
164+
};

src/ui/common/state/FinalityProviderBsnState.tsx

Lines changed: 9 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ import {
1010
getBsnConfig,
1111
type BsnFilterOption,
1212
} from "@/ui/common/services/bsnService";
13+
import {
14+
filterFinalityProvidersByBsn,
15+
isFinalityProviderRowSelectable,
16+
type FinalityProviderFilterState,
17+
} from "@/ui/common/services/finalityProviderService";
1318
import { Bsn } from "@/ui/common/types/bsn";
1419
import {
1520
FinalityProviderState as FinalityProviderStateEnum,
@@ -19,19 +24,12 @@ import { createStateUtils } from "@/ui/common/utils/createStateUtils";
1924

2025
import { getBsnLogoUrl } from "../utils/bsnLogo";
2126

22-
const normalizeHex = (hex?: string): string =>
23-
(hex ?? "").trim().toLowerCase().replace(/^0x/, "");
24-
2527
interface SortState {
2628
field?: string;
2729
direction?: "asc" | "desc";
2830
}
2931

30-
interface FilterState {
31-
searchTerm: string;
32-
providerStatus: "active" | "inactive" | "";
33-
allowlistStatus: "allowlisted" | "not-allowlisted" | "";
34-
}
32+
type FilterState = FinalityProviderFilterState;
3533

3634
const { chainId: BBN_CHAIN_ID } = getNetworkConfigBBN();
3735

@@ -90,107 +88,7 @@ const SORT_DIRECTIONS = {
9088
asc: undefined,
9189
} as const;
9290

93-
// BSN-specific status filters to handle different BSN behaviors
94-
const BABYLON_STATUS_FILTERS = {
95-
active: (fp: FinalityProvider) =>
96-
fp.state === FinalityProviderStateEnum.ACTIVE,
97-
inactive: (fp: FinalityProvider) =>
98-
fp.state === FinalityProviderStateEnum.INACTIVE,
99-
jailed: (fp: FinalityProvider) =>
100-
fp.state === FinalityProviderStateEnum.JAILED,
101-
slashed: (fp: FinalityProvider) =>
102-
fp.state === FinalityProviderStateEnum.SLASHED,
103-
};
104-
105-
const COSMOS_STATUS_FILTERS = {
106-
active: (fp: FinalityProvider) =>
107-
fp.state === FinalityProviderStateEnum.ACTIVE,
108-
inactive: (fp: FinalityProvider) =>
109-
fp.state === FinalityProviderStateEnum.INACTIVE,
110-
};
111-
112-
const ROLLUP_STATUS_FILTERS = {
113-
active: (fp: FinalityProvider) =>
114-
fp.state === FinalityProviderStateEnum.ACTIVE,
115-
inactive: (fp: FinalityProvider) =>
116-
fp.state === FinalityProviderStateEnum.INACTIVE,
117-
slashed: (fp: FinalityProvider) =>
118-
fp.state === FinalityProviderStateEnum.SLASHED,
119-
};
120-
121-
const BSN_STATUS_FILTERS = {
122-
[BBN_CHAIN_ID]: BABYLON_STATUS_FILTERS,
123-
COSMOS: COSMOS_STATUS_FILTERS,
124-
ROLLUP: ROLLUP_STATUS_FILTERS,
125-
};
126-
127-
const createAllowlistFilters = (selectedBsn: Bsn | undefined) => {
128-
const allowSet = new Set((selectedBsn?.allowlist || []).map(normalizeHex));
129-
130-
return {
131-
allowlisted: (fp: FinalityProvider) => allowSet.has(normalizeHex(fp.btcPk)),
132-
"not-allowlisted": (fp: FinalityProvider) =>
133-
!allowSet.has(normalizeHex(fp.btcPk)),
134-
};
135-
};
136-
137-
const filterFinalityProvidersByBsn = (
138-
providers: FinalityProvider[],
139-
filter: FilterState,
140-
selectedBsn: Bsn | undefined,
141-
fpFilterBehavior: "status-based" | "allowlist-based",
142-
): FinalityProvider[] => {
143-
let filtered = providers;
144-
145-
// Apply BSN-aware filtering based on provider status
146-
if (filter.providerStatus) {
147-
if (fpFilterBehavior === "allowlist-based") {
148-
// For rollup BSNs: filter all FPs by allowlist (regardless of active/inactive state)
149-
const allowSet = new Set(
150-
(selectedBsn?.allowlist || []).map(normalizeHex),
151-
);
152-
153-
filtered = filtered.filter((fp) => {
154-
const isAllowlisted = allowSet.has(normalizeHex(fp.btcPk));
155-
156-
if (filter.providerStatus === "active") {
157-
return isAllowlisted;
158-
} else if (filter.providerStatus === "inactive") {
159-
return !isAllowlisted;
160-
} else {
161-
// Handle slashed state for rollups
162-
return fp.state === FinalityProviderStateEnum.SLASHED;
163-
}
164-
});
165-
} else if (fpFilterBehavior === "status-based") {
166-
// For status-based BSNs: filter by finality provider state using BSN-specific filters
167-
const bsnKey =
168-
selectedBsn?.id === BBN_CHAIN_ID
169-
? BBN_CHAIN_ID
170-
: selectedBsn?.type || BBN_CHAIN_ID;
171-
const bsnStatusFilters =
172-
BSN_STATUS_FILTERS[bsnKey] || BSN_STATUS_FILTERS[BBN_CHAIN_ID];
173-
const statusFilter =
174-
bsnStatusFilters[
175-
filter.providerStatus as keyof typeof bsnStatusFilters
176-
];
177-
if (statusFilter) {
178-
filtered = filtered.filter(statusFilter);
179-
}
180-
}
181-
}
182-
183-
if (filter.allowlistStatus) {
184-
const allowlistFilters = createAllowlistFilters(selectedBsn);
185-
const allowlistFilter =
186-
allowlistFilters[filter.allowlistStatus as keyof typeof allowlistFilters];
187-
if (allowlistFilter) {
188-
filtered = filtered.filter(allowlistFilter);
189-
}
190-
}
191-
192-
return filtered;
193-
};
91+
// Component-specific constants remain in the state file
19492

19593
const FILTERS = {
19694
searchTerm: (fp: FinalityProvider, filter: FilterState) => {
@@ -339,21 +237,8 @@ export function FinalityProviderBsnState({ children }: PropsWithChildren) {
339237
}, []);
340238

341239
const isRowSelectable = useCallback(
342-
(row: FinalityProvider) => {
343-
const statusAllowed =
344-
row.state === FinalityProviderStateEnum.ACTIVE ||
345-
row.state === FinalityProviderStateEnum.INACTIVE;
346-
347-
// For selection (not filtering), only restrict based on allowlist for non-Babylon BSNs
348-
const allowlistAllowed =
349-
!selectedBsnId ||
350-
selectedBsnId === BBN_CHAIN_ID ||
351-
(selectedBsn?.allowlist || []).some(
352-
(allowedPk) => normalizeHex(allowedPk) === normalizeHex(row.btcPk),
353-
);
354-
355-
return statusAllowed && allowlistAllowed;
356-
},
240+
(row: FinalityProvider) =>
241+
isFinalityProviderRowSelectable(row, selectedBsnId, selectedBsn),
357242
[selectedBsnId, selectedBsn],
358243
);
359244

0 commit comments

Comments
 (0)