@@ -12,7 +12,10 @@ import {
1212import { formatTimestampToDate } from '~/features/Toucan/Auction/utils/formatting'
1313
1414const mockUseStatsBannerData = vi . fn ( )
15- const mockStoreState = { auctionDetails : null as AuctionDetails | null }
15+ const mockStoreState = {
16+ auctionDetails : null as AuctionDetails | null ,
17+ currentBlockNumber : undefined as number | undefined ,
18+ }
1619
1720vi . mock ( '~/features/Toucan/Auction/hooks/useStatsBannerData' , ( ) => ( {
1821 useStatsBannerData : ( ) => mockUseStatsBannerData ( ) ,
@@ -91,6 +94,7 @@ describe('useAuctionLiquidityLock', () => {
9194 vi . clearAllMocks ( )
9295 mockStatsBanner ( )
9396 mockStoreState . auctionDetails = null
97+ mockStoreState . currentBlockNumber = undefined
9498 } )
9599
96100 it ( 'returns not-locked defaults when the auction has no lock info' , ( ) => {
@@ -149,6 +153,124 @@ describe('useAuctionLiquidityLock', () => {
149153 expect ( result . current . unlockDateFormatted ) . toBe ( formatTimestampToDate ( UNLOCK_TIMESTAMP ) )
150154 } )
151155
156+ it ( 'reads an expired timelock as never-locked' , ( ) => {
157+ mockStoreState . currentBlockNumber = 40000001
158+ mockStoreState . auctionDetails = buildAuctionDetails ( {
159+ poolOwner : POOL_OWNER ,
160+ liquidityLock : {
161+ lockRecipient : LOCK_RECIPIENT ,
162+ lockMode : 1 ,
163+ unlockBlock : '40000000' ,
164+ lpOperator : LP_OPERATOR ,
165+ } ,
166+ } )
167+
168+ const { result } = renderHook ( ( ) => useAuctionLiquidityLock ( ) )
169+
170+ // Lock-status indicators clear: no unlock date, LP owner falls back to the pool owner
171+ expect ( result . current . isLocked ) . toBe ( false )
172+ expect ( result . current . isBuybackEnabled ) . toBe ( false )
173+ expect ( result . current . isFeesForwarder ) . toBe ( false )
174+ expect ( result . current . unlockTimestamp ) . toBeUndefined ( )
175+ expect ( result . current . unlockDateFormatted ) . toBeUndefined ( )
176+ expect ( result . current . lpOwner ) . toBe ( POOL_OWNER )
177+ } )
178+
179+ it ( 'keeps the buyback-burn stat and pill after the lock expires' , ( ) => {
180+ mockStoreState . currentBlockNumber = 40000001
181+ mockStoreState . auctionDetails = buildAuctionDetails ( {
182+ poolOwner : POOL_OWNER ,
183+ liquidityLock : {
184+ lockRecipient : LOCK_RECIPIENT ,
185+ lockMode : 3 ,
186+ unlockBlock : '40000000' ,
187+ lpOperator : LP_OPERATOR ,
188+ totalTokensBurned : BURNED_RAW ,
189+ } ,
190+ } )
191+
192+ const { result } = renderHook ( ( ) => useAuctionLiquidityLock ( ) )
193+
194+ // Lock-status indicators clear on expiry...
195+ expect ( result . current . isLocked ) . toBe ( false )
196+ expect ( result . current . unlockDateFormatted ) . toBeUndefined ( )
197+ expect ( result . current . lpOwner ) . toBe ( POOL_OWNER )
198+ // ...but buyback & burn is a permanent characteristic and must persist
199+ expect ( result . current . isBuybackEnabled ) . toBe ( true )
200+ expect ( result . current . hasBurnedTokens ) . toBe ( true )
201+ expect ( result . current . burnedAmountFormatted ) . toBe ( '1.25M TCAN' )
202+ expect ( result . current . burnedUsdFormatted ) . toBe ( '$187500000' )
203+ } )
204+
205+ it ( 'shows no buyback stat for a never-locked auction' , ( ) => {
206+ mockStoreState . auctionDetails = buildAuctionDetails ( { poolOwner : POOL_OWNER } )
207+
208+ const { result } = renderHook ( ( ) => useAuctionLiquidityLock ( ) )
209+
210+ expect ( result . current . isBuybackEnabled ) . toBe ( false )
211+ expect ( result . current . hasBurnedTokens ) . toBe ( false )
212+ expect ( result . current . burnedAmountFormatted ) . toBeUndefined ( )
213+ } )
214+
215+ it ( 'stays locked when the unlock block has not been reached yet' , ( ) => {
216+ mockStoreState . currentBlockNumber = 39999999
217+ mockStoreState . auctionDetails = buildAuctionDetails ( {
218+ poolOwner : POOL_OWNER ,
219+ liquidityLock : {
220+ lockRecipient : LOCK_RECIPIENT ,
221+ lockMode : 1 ,
222+ unlockBlock : '40000000' ,
223+ lpOperator : LP_OPERATOR ,
224+ } ,
225+ } )
226+
227+ const { result } = renderHook ( ( ) => useAuctionLiquidityLock ( ) )
228+
229+ expect ( result . current . isLocked ) . toBe ( true )
230+ expect ( result . current . lockMode ) . toBe ( AuctionLockMode . Timelock )
231+ expect ( result . current . lpOwner ) . toBe ( LP_OPERATOR )
232+ expect ( result . current . unlockDateFormatted ) . toBe ( formatTimestampToDate ( UNLOCK_TIMESTAMP ) )
233+ } )
234+
235+ it ( 'keeps a permanent lock locked regardless of the current block' , ( ) => {
236+ mockStoreState . currentBlockNumber = 260000000001
237+ mockStoreState . auctionDetails = buildAuctionDetails ( {
238+ liquidityLock : {
239+ lockRecipient : LOCK_RECIPIENT ,
240+ lockMode : 1 ,
241+ unlockBlock : '260000000000' ,
242+ lpOperator : LP_OPERATOR ,
243+ lockedForever : true ,
244+ } ,
245+ } )
246+
247+ const { result } = renderHook ( ( ) => useAuctionLiquidityLock ( ) )
248+
249+ expect ( result . current . isLocked ) . toBe ( true )
250+ expect ( result . current . isPermanentlyLocked ) . toBe ( true )
251+ expect ( result . current . lockMode ) . toBe ( AuctionLockMode . Timelock )
252+ expect ( result . current . lpOwner ) . toBe ( LP_OPERATOR )
253+ } )
254+
255+ it ( 'stays locked while the current block is still loading (no flash-off)' , ( ) => {
256+ mockStoreState . currentBlockNumber = undefined
257+ mockStoreState . auctionDetails = buildAuctionDetails ( {
258+ poolOwner : POOL_OWNER ,
259+ liquidityLock : {
260+ lockRecipient : LOCK_RECIPIENT ,
261+ lockMode : 1 ,
262+ unlockBlock : '40000000' ,
263+ lpOperator : LP_OPERATOR ,
264+ } ,
265+ } )
266+
267+ const { result } = renderHook ( ( ) => useAuctionLiquidityLock ( ) )
268+
269+ expect ( result . current . isLocked ) . toBe ( true )
270+ expect ( result . current . lockMode ) . toBe ( AuctionLockMode . Timelock )
271+ expect ( result . current . lpOwner ) . toBe ( LP_OPERATOR )
272+ } )
273+
152274 it ( 'reads a burn lock as permanently locked with no unlock date or LP owner' , ( ) => {
153275 mockStoreState . auctionDetails = buildAuctionDetails ( {
154276 poolOwner : POOL_OWNER ,
0 commit comments