FIX - recover expired crypto quotes

Expire client-side trade quotes at their server deadline and clear rejected execution state so browser debug sessions can request and execute a fresh quote.
This commit is contained in:
smx.pusha
2026-08-18 10:22:44 +02:00
parent bad7585ab7
commit e517a8fae8
2 changed files with 43 additions and 1 deletions
+27
View File
@@ -81,6 +81,33 @@ describe('crypto store', () => {
expect(crypto.pendingQuote).toBeNull()
})
it('removes an expired quote so the trade form can request a new one', async () => {
vi.useFakeTimers()
const expiringQuote = { ...quote, expiresAt: Date.now() + 1000 }
mockNuiCall.mockResolvedValueOnce({ data: expiringQuote, success: true })
const crypto = useCryptoStore()
await crypto.quote('aurora', 'buy', '1')
expect(crypto.pendingQuote).toEqual(expiringQuote)
await vi.advanceTimersByTimeAsync(1000)
expect(crypto.pendingQuote).toBeNull()
vi.useRealTimers()
})
it('clears a rejected execution so a fresh quote can be requested', async () => {
mockNuiCall.mockResolvedValueOnce({
error: 'quote_expired',
success: false,
})
const crypto = useCryptoStore()
crypto.pendingQuote = quote
expect(await crypto.executeQuote()).toBe(false)
expect(crypto.pendingQuote).toBeNull()
expect(crypto.error).toBe('quote_expired')
})
it('keeps server errors and does not replace portfolio state', async () => {
mockNuiCall.mockResolvedValueOnce({
error: 'quote_expired',
+16 -1
View File
@@ -82,6 +82,21 @@ export const useCryptoStore = defineStore('crypto', {
side,
})
this.pendingQuote = response.success ? (response.data ?? null) : null
if (this.pendingQuote) {
const quoteId = this.pendingQuote.id
const expiresAt = this.pendingQuote.expiresAt
globalThis.setTimeout(
() => {
if (
this.pendingQuote?.id === quoteId &&
this.pendingQuote.expiresAt <= Date.now()
) {
this.pendingQuote = null
}
},
Math.max(0, expiresAt - Date.now()),
)
}
return response
},
async executeQuote(): Promise<boolean> {
@@ -90,9 +105,9 @@ export const useCryptoStore = defineStore('crypto', {
idempotencyKey: requestKey('trade'),
quoteId: this.pendingQuote.id,
})
this.pendingQuote = null
if (!response.success || !response.data) return false
this.data = response.data
this.pendingQuote = null
return true
},
async updateProfile(payload: {