@@ -6,49 +6,14 @@ import {
66 Text ,
77} from "@babylonlabs-io/core-ui" ;
88import { useMemo , useState } from "react" ;
9- import { BiSolidBadgeCheck } from "react-icons/bi" ;
109
10+ import { ActivityCard } from "@/ui/common/components/ActivityCard/ActivityCard" ;
11+ import { transformDelegationToVerifiedExpansionCard } from "@/ui/common/components/ActivityCard/utils/activityCardTransformers" ;
1112import { ResponsiveDialog } from "@/ui/common/components/Modals/ResponsiveDialog" ;
12- import { getNetworkConfigBBN } from "@/ui/common/config/network/bbn" ;
13- import { getNetworkConfigBTC } from "@/ui/common/config/network/btc" ;
14- import { EXPANSION_OPERATIONS } from "@/ui/common/constants" ;
1513import { useVerifiedStakingExpansionService } from "@/ui/common/hooks/services/useVerifiedStakingExpansionService" ;
1614import { useDelegationV2State } from "@/ui/common/state/DelegationV2State" ;
17- import { useFinalityProviderBsnState } from "@/ui/common/state/FinalityProviderBsnState" ;
1815import { useFinalityProviderState } from "@/ui/common/state/FinalityProviderState" ;
1916import { DelegationV2 } from "@/ui/common/types/delegationsV2" ;
20- import { satoshiToBtc } from "@/ui/common/utils/btc" ;
21- import { maxDecimals } from "@/ui/common/utils/maxDecimals" ;
22- import { trim } from "@/ui/common/utils/trim" ;
23-
24- const { chainId : BBN_CHAIN_ID } = getNetworkConfigBBN ( ) ;
25- const { coinSymbol } = getNetworkConfigBTC ( ) ;
26-
27- // Helper function to determine expansion operation type
28- const getExpansionType = (
29- expansion : DelegationV2 ,
30- original : DelegationV2 | undefined ,
31- ) => {
32- if ( ! original ) {
33- // If we can't find original, assume it's adding BSN/FP based on having previous tx
34- return EXPANSION_OPERATIONS . ADD_BSN_FP ;
35- }
36-
37- const newFPs = expansion . finalityProviderBtcPksHex . filter (
38- ( fp ) => ! original . finalityProviderBtcPksHex . includes ( fp ) ,
39- ) ;
40- const fpsChanged = newFPs . length > 0 ;
41-
42- if ( fpsChanged ) {
43- return EXPANSION_OPERATIONS . ADD_BSN_FP ; // Adding BSN/FP (always includes timelock renewal)
44- } else {
45- return EXPANSION_OPERATIONS . RENEW_TIMELOCK ; // Pure timelock renewal only
46- }
47-
48- // TODO: Future expansion types to consider:
49- // - INCREASE_AMOUNT (when staking amount > original amount)
50- // - Mixed operations may be possible in future versions
51- } ;
5217
5318interface VerifiedStakeExpansionModalProps {
5419 open : boolean ;
@@ -68,173 +33,52 @@ function VerifiedExpansionItem({
6833 processing,
6934} : VerifiedExpansionItemProps ) {
7035 const { findDelegationByTxHash } = useDelegationV2State ( ) ;
71- const { getRegisteredFinalityProvider } = useFinalityProviderState ( ) ;
72- const { bsnList } = useFinalityProviderBsnState ( ) ;
73-
74- // Parse BSN and FP information with proper expansion type detection
75- const { bsnFpPairs, newCount, operationType, originalDelegation } =
76- useMemo ( ( ) => {
77- const pairs : Array < { bsnName : string ; fpName : string ; isNew : boolean } > =
78- [ ] ;
79-
80- // Find the original delegation if this is an expansion
81- const original = delegation . previousStakingTxHashHex
82- ? findDelegationByTxHash ( delegation . previousStakingTxHashHex )
83- : undefined ;
84-
85- // Determine the operation type
86- const opType = getExpansionType ( delegation , original ) ;
87-
88- // Create BSN/FP pairs with correct "new" detection
89- delegation . finalityProviderBtcPksHex . forEach ( ( fpPkHex ) => {
90- const provider = getRegisteredFinalityProvider ( fpPkHex ) ;
91- const bsnId = provider ?. bsnId || BBN_CHAIN_ID ;
92- const bsn = bsnList . find ( ( b ) => b . id === bsnId ) ;
93-
94- // Determine if this FP is new by checking if it exists in original delegation
95- const isNewFP = original
96- ? ! original . finalityProviderBtcPksHex . includes ( fpPkHex )
97- : false ; // If no original delegation found, don't mark as new
98-
99- pairs . push ( {
100- bsnName : bsn ?. name || "Babylon Genesis" ,
101- fpName : provider ?. description ?. moniker || trim ( fpPkHex , 8 ) ,
102- isNew : isNewFP ,
103- } ) ;
104- } ) ;
36+ const { finalityProviderMap } = useFinalityProviderState ( ) ;
37+
38+ // Transform delegation to activity card data
39+ const activityCardData = useMemo ( ( ) => {
40+ // Find the original delegation - this should always exist for verified expansions
41+ const originalDelegation = delegation . previousStakingTxHashHex
42+ ? findDelegationByTxHash ( delegation . previousStakingTxHashHex )
43+ : undefined ;
44+
45+ if ( ! originalDelegation ) {
46+ console . error (
47+ "Original delegation not found for verified expansion:" ,
48+ delegation . stakingTxHashHex ,
49+ "previousTxHash:" ,
50+ delegation . previousStakingTxHashHex ,
51+ ) ;
52+ // This should not happen for verified expansions, but return a fallback
53+ throw new Error (
54+ `Invalid verified expansion: original delegation not found for ${ delegation . stakingTxHashHex } ` ,
55+ ) ;
56+ }
10557
106- const newPairsCount = pairs . filter ( ( p ) => p . isNew ) . length ;
107- return {
108- bsnFpPairs : pairs ,
109- newCount : newPairsCount ,
110- operationType : opType ,
111- originalDelegation : original ,
112- } ;
113- } , [
58+ return transformDelegationToVerifiedExpansionCard (
11459 delegation ,
115- findDelegationByTxHash ,
116- getRegisteredFinalityProvider ,
117- bsnList ,
118- ] ) ;
119-
120- const stakingAmount = maxDecimals ( satoshiToBtc ( delegation . stakingAmount ) , 8 ) ;
60+ originalDelegation ,
61+ finalityProviderMap ,
62+ ) ;
63+ } , [ delegation , findDelegationByTxHash , finalityProviderMap ] ) ;
64+
65+ // Create the activity card data with primary action
66+ const activityCardDataWithAction = {
67+ ...activityCardData ,
68+ primaryAction : {
69+ label : "Expand" ,
70+ onClick : onExpand ,
71+ variant : "contained" as const ,
72+ size : "small" as const ,
73+ disabled : processing ,
74+ } ,
75+ } ;
12176
12277 return (
123- < div className = "space-y-3 rounded-lg border border-secondary-strokeLight p-4" >
124- < div className = "flex items-start justify-between" >
125- < div className = "flex-1 space-y-2" >
126- < div className = "flex items-center gap-2" >
127- < BiSolidBadgeCheck className = "text-xl text-primary-light" />
128- < Text variant = "body1" className = "font-medium text-accent-primary" >
129- { operationType === EXPANSION_OPERATIONS . RENEW_TIMELOCK
130- ? "Timelock Renewal"
131- : operationType === EXPANSION_OPERATIONS . ADD_BSN_FP
132- ? "BSN/FP Expansion"
133- : "Verified Expansion" }
134- </ Text >
135- </ div >
136-
137- < div className = "space-y-1" >
138- < div className = "flex items-center gap-2" >
139- < Text variant = "body2" className = "text-accent-secondary" >
140- Amount:
141- </ Text >
142- < Text variant = "body2" className = "text-accent-primary" >
143- { stakingAmount } { coinSymbol }
144- </ Text >
145- </ div >
146-
147- < div className = "flex items-center gap-2" >
148- < Text variant = "body2" className = "text-accent-secondary" >
149- Transaction:
150- </ Text >
151- < Text variant = "body2" className = "font-mono text-accent-primary" >
152- { trim ( delegation . stakingTxHashHex , 10 ) }
153- </ Text >
154- </ div >
155-
156- { /* Show different information based on operation type */ }
157- { operationType === EXPANSION_OPERATIONS . RENEW_TIMELOCK &&
158- originalDelegation && (
159- < div className = "flex items-center gap-2" >
160- < Text variant = "body2" className = "text-accent-secondary" >
161- Timelock:
162- </ Text >
163- < Text variant = "body2" className = "text-accent-primary" >
164- { originalDelegation . stakingTimelock . toLocaleString ( ) } blocks
165- → { delegation . stakingTimelock . toLocaleString ( ) } blocks
166- </ Text >
167- </ div >
168- ) }
169-
170- { operationType === EXPANSION_OPERATIONS . ADD_BSN_FP && (
171- < >
172- { newCount > 0 && (
173- < div className = "flex items-center gap-2" >
174- < Text variant = "body2" className = "text-accent-secondary" >
175- New BSN/FP pairs:
176- </ Text >
177- < Text variant = "body2" className = "text-accent-primary" >
178- { newCount }
179- </ Text >
180- </ div >
181- ) }
182- { originalDelegation &&
183- originalDelegation . stakingTimelock !==
184- delegation . stakingTimelock && (
185- < div className = "flex items-center gap-2" >
186- < Text variant = "body2" className = "text-accent-secondary" >
187- Timelock renewed to:
188- </ Text >
189- < Text variant = "body2" className = "text-accent-primary" >
190- { delegation . stakingTimelock . toLocaleString ( ) } blocks
191- </ Text >
192- </ div >
193- ) }
194- </ >
195- ) }
196- </ div >
197-
198- { /* Show BSN/FP pairs - different display for different operation types */ }
199- < div className = "mt-3 space-y-1" >
200- < Text variant = "body2" className = "mb-1 text-accent-secondary" >
201- BSN / Finality Provider pairs:
202- </ Text >
203- < div className = "space-y-1" >
204- { bsnFpPairs . map ( ( pair , index ) => (
205- < div key = { index } className = "flex items-center gap-2 text-sm" >
206- < Text
207- variant = "body2"
208- className = {
209- pair . isNew ? "text-primary-light" : "text-accent-primary"
210- }
211- >
212- { pair . bsnName } / { pair . fpName }
213- </ Text >
214- { /* Only show NEW labels for ADD_BSN_FP operations and actually new pairs */ }
215- { operationType === EXPANSION_OPERATIONS . ADD_BSN_FP &&
216- pair . isNew && (
217- < span className = "rounded bg-primary-light/10 px-2 py-0.5 text-xs text-primary-light" >
218- NEW
219- </ span >
220- ) }
221- </ div >
222- ) ) }
223- </ div >
224- </ div >
225- </ div >
226-
227- < Button
228- variant = "contained"
229- size = "small"
230- onClick = { onExpand }
231- disabled = { processing }
232- className = "ml-4"
233- >
234- Expand
235- </ Button >
236- </ div >
237- </ div >
78+ < ActivityCard
79+ data = { activityCardDataWithAction }
80+ className = "border border-secondary-strokeLight"
81+ />
23882 ) ;
23983}
24084
0 commit comments