Skip to content

Commit 397cc8a

Browse files
authored
Merge pull request #1378 from peanutprotocol/fix/pots-balance-check
revert balance fix - request pots
2 parents d1ada95 + d617aa4 commit 397cc8a

2 files changed

Lines changed: 31 additions & 47 deletions

File tree

src/components/Payment/PaymentForm/index.tsx

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,8 @@ import { useAppDispatch, usePaymentStore } from '@/redux/hooks'
2525
import { paymentActions } from '@/redux/slices/payment-slice'
2626
import { walletActions } from '@/redux/slices/wallet-slice'
2727
import { areEvmAddressesEqual, ErrorHandler, formatAmount, formatCurrency, getContributorsFromCharge } from '@/utils'
28-
import { initializeAppKit } from '@/config/wagmi.config'
2928
import { useAppKit, useDisconnect } from '@reown/appkit/react'
30-
import * as Sentry from '@sentry/nextjs'
29+
import { initializeAppKit } from '@/config/wagmi.config'
3130
import Image from 'next/image'
3231
import { useRouter, useSearchParams } from 'next/navigation'
3332
import { useCallback, useContext, useEffect, useMemo, useState } from 'react'
@@ -41,6 +40,7 @@ import { invitesApi } from '@/services/invites'
4140
import { EInviteType } from '@/services/services.types'
4241
import ContributorCard from '@/components/Global/Contributors/ContributorCard'
4342
import { getCardPosition } from '@/components/Global/Card'
43+
import * as Sentry from '@sentry/nextjs'
4444

4545
export type PaymentFlowProps = {
4646
isExternalWalletFlow?: boolean
@@ -237,8 +237,12 @@ export const PaymentForm = ({
237237
}
238238
} else {
239239
// regular send/pay
240-
if (isActivePeanutWallet && areEvmAddressesEqual(selectedTokenAddress, PEANUT_WALLET_TOKEN)) {
241-
// peanut wallet payment - ALWAYS check balance (including request pots)
240+
if (
241+
!showRequestPotInitialView && // don't apply balance check on request pot payment initial view
242+
isActivePeanutWallet &&
243+
areEvmAddressesEqual(selectedTokenAddress, PEANUT_WALLET_TOKEN)
244+
) {
245+
// peanut wallet payment
242246
const walletNumeric = parseFloat(String(peanutWalletBalance).replace(/,/g, ''))
243247
if (walletNumeric < parsedInputAmount) {
244248
dispatch(paymentActions.setError('Insufficient balance'))
@@ -370,8 +374,8 @@ export const PaymentForm = ({
370374
if (inviteError) {
371375
setInviteError(false)
372376
}
373-
// Handle insufficient balance - redirect to add money
374-
if (isActivePeanutWallet && isInsufficientBalanceError && !isExternalWalletFlow) {
377+
// Invites will be handled in the payment page, skip this step for request pots initial view
378+
if (!showRequestPotInitialView && isActivePeanutWallet && isInsufficientBalanceError && !isExternalWalletFlow) {
375379
// If the user doesn't have app access, accept the invite before claiming the link
376380
if (recipient.recipientType === 'USERNAME' && !user?.user.hasAppAccess) {
377381
const isAccepted = await handleAcceptInvite()
@@ -393,7 +397,6 @@ export const PaymentForm = ({
393397
extra: { flow: 'external_wallet_payment' },
394398
})
395399
}
396-
return
397400
}
398401

399402
// skip this step for request pots initial view
@@ -521,7 +524,10 @@ export const PaymentForm = ({
521524
return 'Send'
522525
}
523526

524-
// Check insufficient balance BEFORE other conditions
527+
if (showRequestPotInitialView) {
528+
return 'Pay'
529+
}
530+
525531
if (isActivePeanutWallet && isInsufficientBalanceError && !isExternalWalletFlow) {
526532
return (
527533
<div className="flex items-center gap-1">
@@ -534,10 +540,6 @@ export const PaymentForm = ({
534540
)
535541
}
536542

537-
if (showRequestPotInitialView) {
538-
return 'Pay'
539-
}
540-
541543
if (isActivePeanutWallet) {
542544
return (
543545
<div className="flex items-center gap-1">
@@ -554,11 +556,12 @@ export const PaymentForm = ({
554556
}
555557

556558
const getButtonIcon = (): IconName | undefined => {
557-
if (!isExternalWalletConnected && isExternalWalletFlow) return 'wallet-outline'
559+
if (!showRequestPotInitialView && !isExternalWalletConnected && isExternalWalletFlow) return 'wallet-outline'
558560

559-
if (isActivePeanutWallet && isInsufficientBalanceError && !isExternalWalletFlow) return 'arrow-down'
561+
if (!showRequestPotInitialView && isActivePeanutWallet && isInsufficientBalanceError && !isExternalWalletFlow)
562+
return 'arrow-down'
560563

561-
if (!isProcessing && isActivePeanutWallet && !isExternalWalletFlow && !showRequestPotInitialView)
564+
if (!showRequestPotInitialView && !isProcessing && isActivePeanutWallet && !isExternalWalletFlow)
562565
return 'arrow-up-right'
563566

564567
return undefined

src/hooks/usePaymentInitiator.ts

Lines changed: 13 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { PEANUT_WALLET_CHAIN, PEANUT_WALLET_TOKEN } from '@/constants'
2-
import { BALANCE_DECREASE, INITIATE_PAYMENT } from '@/constants/query.consts'
32
import { tokenSelectorContext } from '@/context'
43
import { useWallet } from '@/hooks/wallet/useWallet'
54
import { type ParsedURL } from '@/lib/url-parser/types/payment'
@@ -27,7 +26,6 @@ import { getRoute, type PeanutCrossChainRoute } from '@/services/swap'
2726
import { estimateTransactionCostUsd } from '@/app/actions/tokens'
2827
import { captureException } from '@sentry/nextjs'
2928
import { useRouter } from 'next/navigation'
30-
import { useQueryClient } from '@tanstack/react-query'
3129

3230
enum ELoadingStep {
3331
IDLE = 'Idle',
@@ -84,7 +82,6 @@ export const usePaymentInitiator = () => {
8482
const router = useRouter()
8583
const config = useConfig()
8684
const { chain: connectedWalletChain } = useWagmiAccount()
87-
const queryClient = useQueryClient()
8885

8986
const [slippagePercentage, setSlippagePercentage] = useState<number | undefined>(undefined)
9087
const [unsignedTx, setUnsignedTx] = useState<peanutInterfaces.IPeanutUnsignedTransaction | null>(null)
@@ -605,22 +602,12 @@ export const usePaymentInitiator = () => {
605602
]
606603
)
607604

608-
// @dev Architecture Note: initiatePayment flow and mutation tracking
609-
//
610-
// Current: This async function uses state-based lifecycle (isProcessing, loadingStep)
611-
// rather than TanStack Query mutations. This is INTENTIONAL because:
612-
// 1. It has two phases: charge preparation (no balance change) + payment execution (balance decrease)
613-
// 2. Only the payment execution phase triggers balance-decreasing mutations (via sendMoney/sendTransactions)
614-
// 3. sendMoney already properly wraps mutations with mutationKey: [BALANCE_DECREASE, SEND_MONEY]
615-
// 4. usePendingTransactions tracks ALL balance-decreasing operations globally
616-
//
617-
// Bug fix (2025-10): Added guard at line 648 to prevent premature payment execution when
618-
// fetching existing charges. Previously, fetching an existing charge (chargeCreated=false)
619-
// without skipChargeCreation=true would fall through and trigger sendMoney() prematurely,
620-
// causing optimistic balance updates before user confirmed payment.
621-
//
622-
// Future consideration: Could wrap entire initiatePayment in useMutation, but complexity is HIGH
623-
// due to two-phase flow, Redux integration, and multiple return points. Current architecture works.
605+
// @dev TODO: Refactor to TanStack Query mutation for architectural consistency
606+
// Current: This async function works correctly (protected by isProcessing state)
607+
// but is NOT tracked by usePendingTransactions mutation system.
608+
// Future improvement: Wrap in useMutation for consistency with other balance-decreasing ops.
609+
// mutationKey: [BALANCE_DECREASE, INITIATE_PAYMENT]
610+
// Complexity: HIGH - complex state/Redux integration. Low priority.
624611
//
625612
// initiate and process payments
626613
const initiatePayment = useCallback(
@@ -641,25 +628,19 @@ export const usePaymentInitiator = () => {
641628
console.log('Proceeding with charge details:', determinedChargeDetails.uuid)
642629

643630
// 2. handle charge state
644-
// Return early if:
645-
// a) Explicitly told to return after charge creation (request pot initial view)
646-
// b) Charge was just created AND needs special handling (external wallet/cross-chain)
647-
// c) Fetching existing charge WITHOUT explicit skipChargeCreation (not from CONFIRM view)
648-
const shouldReturnAfterCharge =
649-
payload.returnAfterChargeCreation ||
631+
if (
632+
payload.returnAfterChargeCreation || // For request pot payment, return after charge creation
650633
(chargeCreated &&
651634
(payload.isExternalWalletFlow ||
652635
!isPeanutWallet ||
653636
(isPeanutWallet &&
654637
(!areEvmAddressesEqual(determinedChargeDetails.tokenAddress, PEANUT_WALLET_TOKEN) ||
655-
determinedChargeDetails.chainId !== PEANUT_WALLET_CHAIN.id.toString())))) ||
656-
// NEW: If charge exists (not created) and we're NOT explicitly skipping charge creation,
657-
// then we're in "prepare" mode and shouldn't execute payment yet
658-
(!chargeCreated && payload.chargeId && !payload.skipChargeCreation)
659-
660-
if (shouldReturnAfterCharge) {
638+
determinedChargeDetails.chainId !== PEANUT_WALLET_CHAIN.id.toString()))))
639+
) {
661640
console.log(
662-
`Charge ready. Returning without payment execution. (chargeCreated: ${chargeCreated}, skipChargeCreation: ${payload.skipChargeCreation})`
641+
`Charge created. Transitioning to Confirm view for: ${
642+
payload.isExternalWalletFlow ? 'Add Money Flow' : 'External Wallet'
643+
}.`
663644
)
664645
setLoadingStep('Charge Created')
665646
return { status: 'Charge Created', charge: determinedChargeDetails, success: false }

0 commit comments

Comments
 (0)