From 559d17081f02f42d2b197544e7db3a0e397b4425 Mon Sep 17 00:00:00 2001 From: Raymond Jacobson Date: Wed, 17 Dec 2025 16:07:28 -0800 Subject: [PATCH 1/3] Check two tx --- api/v1_prizes_claim.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/api/v1_prizes_claim.go b/api/v1_prizes_claim.go index 8e7f0147..29c3b975 100644 --- a/api/v1_prizes_claim.go +++ b/api/v1_prizes_claim.go @@ -133,15 +133,16 @@ func (app *ApiServer) v1PrizesClaim(c *fiber.Ctx) error { default: } - var exists bool + var count int + // Wait for 2 balance changes to appear in the database (send & receive) err := app.pool.QueryRow(ctx, ` - SELECT EXISTS(SELECT 1 FROM sol_token_account_balance_changes WHERE signature = $1) - `, req.Signature).Scan(&exists) + SELECT COUNT(*) FROM sol_token_account_balance_changes WHERE signature = $1 + `, req.Signature).Scan(&count) if err != nil { app.logger.Error("Failed to query transaction existence", zap.Error(err)) return fiber.NewError(fiber.StatusInternalServerError, "Database error") } - if exists { + if count >= 2 { break } time.Sleep(200 * time.Millisecond) // Wait before retrying From 71605f1f0b7ec62158806668592b477c60716370 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Wed, 17 Dec 2025 16:29:46 -0800 Subject: [PATCH 2/3] Fix tests --- api/v1_prizes_claim.go | 9 ++++++++ api/v1_prizes_claim_test.go | 45 +++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/api/v1_prizes_claim.go b/api/v1_prizes_claim.go index 29c3b975..845a0ce4 100644 --- a/api/v1_prizes_claim.go +++ b/api/v1_prizes_claim.go @@ -123,6 +123,8 @@ func (app *ApiServer) v1PrizesClaim(c *fiber.Ctx) error { } // Wait for the transaction to appear in sol_token_account_balance_changes to ensure no race conditions + startTime := time.Now() + maxWaitForNonExistent := 2 * time.Second // Fail faster if transaction doesn't exist at all for { select { case <-ctx.Done(): @@ -145,6 +147,13 @@ func (app *ApiServer) v1PrizesClaim(c *fiber.Ctx) error { if count >= 2 { break } + // If we've waited a reasonable time and count is still 0, the transaction likely doesn't exist + if count == 0 && time.Since(startTime) > maxWaitForNonExistent { + app.logger.Error("Transaction not found after waiting", + zap.String("wallet", req.Wallet), + zap.String("signature", req.Signature)) + return fiber.NewError(fiber.StatusRequestTimeout, "Request timed out") + } time.Sleep(200 * time.Millisecond) // Wait before retrying } diff --git a/api/v1_prizes_claim_test.go b/api/v1_prizes_claim_test.go index a58c2c47..05f8a736 100644 --- a/api/v1_prizes_claim_test.go +++ b/api/v1_prizes_claim_test.go @@ -280,6 +280,15 @@ func TestV1PrizesClaim(t *testing.T) { `, "wrong_mint_sig", otherMint, validWallet, "account3", -yakClaimAmount, 1000000000000, 12347, time.Now()) require.NoError(t, err) + // Insert second balance change to pass the wait loop (but with wrong mint, so validation will fail) + _, err = app.writePool.Exec(ctx, ` + INSERT INTO sol_token_account_balance_changes + (signature, mint, owner, account, change, balance, slot, block_timestamp) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + ON CONFLICT DO NOTHING + `, "wrong_mint_sig", otherMint, prizeReceiverAddress, "receiver_account3", yakClaimAmount, 1000000000000, 12347, time.Now()) + require.NoError(t, err) + requestBody := PrizeClaimRequest{ Signature: "wrong_mint_sig", Wallet: validWallet, @@ -306,6 +315,15 @@ func TestV1PrizesClaim(t *testing.T) { `, "wrong_amount_sig", yakMintAddress, validWallet, "account_wrong_amount", -wrongAmount, 1000000000000, 12350, time.Now()) require.NoError(t, err) + // Insert second balance change to pass the wait loop (but with wrong amount, so validation will fail) + _, err = app.writePool.Exec(ctx, ` + INSERT INTO sol_token_account_balance_changes + (signature, mint, owner, account, change, balance, slot, block_timestamp) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + ON CONFLICT DO NOTHING + `, "wrong_amount_sig", yakMintAddress, prizeReceiverAddress, "receiver_account_wrong_amount", wrongAmount, 1000000000000, 12350, time.Now()) + require.NoError(t, err) + requestBody := PrizeClaimRequest{ Signature: "wrong_amount_sig", Wallet: validWallet, @@ -331,6 +349,15 @@ func TestV1PrizesClaim(t *testing.T) { `, "wrong_wallet_sig", yakMintAddress, otherWallet, "account4", -yakClaimAmount, 1000000000000, 12348, time.Now()) require.NoError(t, err) + // Insert second balance change to pass the wait loop (but with wrong wallet, so validation will fail) + _, err = app.writePool.Exec(ctx, ` + INSERT INTO sol_token_account_balance_changes + (signature, mint, owner, account, change, balance, slot, block_timestamp) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + ON CONFLICT DO NOTHING + `, "wrong_wallet_sig", yakMintAddress, prizeReceiverAddress, "receiver_account4", yakClaimAmount, 1000000000000, 12348, time.Now()) + require.NoError(t, err) + requestBody := PrizeClaimRequest{ Signature: "wrong_wallet_sig", Wallet: validWallet, // Different wallet @@ -405,6 +432,15 @@ func TestV1PrizesClaim(t *testing.T) { `, "positive_change_sig", yakMintAddress, validWallet, "account7", yakClaimAmount, 1000000000000, 12351, time.Now()) require.NoError(t, err) + // Insert second balance change to pass the wait loop (but user has positive change, so validation will fail) + _, err = app.writePool.Exec(ctx, ` + INSERT INTO sol_token_account_balance_changes + (signature, mint, owner, account, change, balance, slot, block_timestamp) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + ON CONFLICT DO NOTHING + `, "positive_change_sig", yakMintAddress, prizeReceiverAddress, "receiver_account7", -yakClaimAmount, 1000000000000, 12351, time.Now()) + require.NoError(t, err) + requestBody := PrizeClaimRequest{ Signature: "positive_change_sig", Wallet: validWallet, @@ -430,6 +466,15 @@ func TestV1PrizesClaim(t *testing.T) { `, "zero_change_sig", yakMintAddress, validWallet, "account8", 0, 1000000000000, 12352, time.Now()) require.NoError(t, err) + // Insert second balance change to pass the wait loop (but user has zero change, so validation will fail) + _, err = app.writePool.Exec(ctx, ` + INSERT INTO sol_token_account_balance_changes + (signature, mint, owner, account, change, balance, slot, block_timestamp) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + ON CONFLICT DO NOTHING + `, "zero_change_sig", yakMintAddress, prizeReceiverAddress, "receiver_account8", yakClaimAmount, 1000000000000, 12352, time.Now()) + require.NoError(t, err) + requestBody := PrizeClaimRequest{ Signature: "zero_change_sig", Wallet: validWallet, From 8a9b5be9ce183aaf55c9789e3a9a187263d1d7fa Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Wed, 17 Dec 2025 16:32:52 -0800 Subject: [PATCH 3/3] revert prizes claim --- api/v1_prizes_claim.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/api/v1_prizes_claim.go b/api/v1_prizes_claim.go index 845a0ce4..29c3b975 100644 --- a/api/v1_prizes_claim.go +++ b/api/v1_prizes_claim.go @@ -123,8 +123,6 @@ func (app *ApiServer) v1PrizesClaim(c *fiber.Ctx) error { } // Wait for the transaction to appear in sol_token_account_balance_changes to ensure no race conditions - startTime := time.Now() - maxWaitForNonExistent := 2 * time.Second // Fail faster if transaction doesn't exist at all for { select { case <-ctx.Done(): @@ -147,13 +145,6 @@ func (app *ApiServer) v1PrizesClaim(c *fiber.Ctx) error { if count >= 2 { break } - // If we've waited a reasonable time and count is still 0, the transaction likely doesn't exist - if count == 0 && time.Since(startTime) > maxWaitForNonExistent { - app.logger.Error("Transaction not found after waiting", - zap.String("wallet", req.Wallet), - zap.String("signature", req.Signature)) - return fiber.NewError(fiber.StatusRequestTimeout, "Request timed out") - } time.Sleep(200 * time.Millisecond) // Wait before retrying }