diff --git a/src/components/TransactionDetails/__tests__/fixtures/render-baseline.json b/src/components/TransactionDetails/__tests__/fixtures/render-baseline.json index b3b2a0014..cc3796610 100644 --- a/src/components/TransactionDetails/__tests__/fixtures/render-baseline.json +++ b/src/components/TransactionDetails/__tests__/fixtures/render-baseline.json @@ -2697,7 +2697,7 @@ "kind": "PERK_REWARD", "txHash": "", "perkReward": { - "reason": "✨ Peanut Mystery Rewards", + "reason": "\u2728 Peanut Mystery Rewards", "discountPercentage": 100, "originatingTxId": null, "originatingTxType": "MANUAL", @@ -3167,9 +3167,9 @@ "price_details": { "rate": 4597916.4, "currency": "ARS", - "base_amount": 0.000026751248, + "base_amount": 2.6751248e-05, "paid_amount": 0, - "final_amount": 0.000026751248, + "final_amount": 2.6751248e-05, "discount_rate": 0, "currency_amount": 123, "currency_final_amount": 123 @@ -3190,9 +3190,9 @@ "price_details": { "rate": 4597916.4, "currency": "ARS", - "base_amount": 0.000026751248, + "base_amount": 2.6751248e-05, "paid_amount": 0, - "final_amount": 0.000026751248, + "final_amount": 2.6751248e-05, "discount_rate": 0, "currency_amount": 123, "currency_final_amount": 123 @@ -3236,9 +3236,9 @@ "price_details": { "rate": 138734817.45, "currency": "ARS", - "base_amount": 8.9e-7, + "base_amount": 8.9e-07, "paid_amount": 0, - "final_amount": 8.9e-7, + "final_amount": 8.9e-07, "discount_rate": 0, "currency_amount": 123, "currency_final_amount": 123 @@ -3540,9 +3540,9 @@ "createdAt": "" }, "expected": { - "direction": "send", - "userName": "", - "transactionCardType": "send", + "direction": "receive", + "userName": "", + "transactionCardType": "receive", "provider": "PEANUT", "cardPaymentDefined": false, "bankAccountDetailsDefined": false diff --git a/src/components/TransactionDetails/strategies/registry.ts b/src/components/TransactionDetails/strategies/registry.ts index 50a57a907..035622ea1 100644 --- a/src/components/TransactionDetails/strategies/registry.ts +++ b/src/components/TransactionDetails/strategies/registry.ts @@ -24,6 +24,7 @@ import { intentFallback } from './fallback' export type IntentKind = | 'DIRECT_TRANSFER' | 'SEND_LINK' + | 'SEND_LINK_CLAIM' | 'P2P_REQUEST_FULFILL' | 'QR_PAY' | 'CRYPTO_DEPOSIT' @@ -39,6 +40,7 @@ const STRATEGIES: Record = { DIRECT_TRANSFER: p2pSendOrRequestFulfill, P2P_REQUEST_FULFILL: p2pSendOrRequestFulfill, SEND_LINK: sendLink, + SEND_LINK_CLAIM: sendLink, QR_PAY: qrPay, CRYPTO_DEPOSIT: cryptoDeposit, CRYPTO_WITHDRAW: cryptoWithdraw, diff --git a/src/hooks/useHomeCarouselCTAs.tsx b/src/hooks/useHomeCarouselCTAs.tsx index 1dea3402e..87acbbfa2 100644 --- a/src/hooks/useHomeCarouselCTAs.tsx +++ b/src/hooks/useHomeCarouselCTAs.tsx @@ -109,6 +109,7 @@ export const useHomeCarouselCTAs = () => { [latestHistory] ) const hasSentInvites = (user?.invitesSent?.length ?? 0) > 0 + const hasSupportSurvivorBadge = user?.user?.badges?.some((b) => b.code === 'SUPPORT_SURVIVOR') ?? false const dismissCTA = useCallback( (ctaId: string) => { @@ -261,11 +262,12 @@ export const useHomeCarouselCTAs = () => { }) } - // Bug bounty — shown to activated users only. Server enforces the real - // eligibility (email verified, ≥1 payment OR KYC approved), lifetime - // caps, and the daily budget. This gate just keeps the CTA off cold - // accounts where the reward would be denied anyway. - if (isActivated) { + // Bug bounty — shown to activated users who haven't already claimed. + // Server enforces lifetime cap of 1 grant per user, so re-pinging the + // CTA after a successful claim would just bounce off `already_granted`. + // Hide once the SUPPORT_SURVIVOR badge is on the user — that's the + // server-side dedup marker, so it's the authoritative signal. + if (isActivated && !hasSupportSurvivorBadge) { _carouselCTAs.push({ id: 'bug-bounty', title: ( @@ -329,6 +331,7 @@ export const useHomeCarouselCTAs = () => { isActivated, hasMadeQrPayment, hasSentInvites, + hasSupportSurvivorBadge, oneSignalInitialized, setIsIosPwaInstallModalOpen, toast, diff --git a/src/hooks/wallet/useGrantSessionKey.ts b/src/hooks/wallet/useGrantSessionKey.ts index 9228405b4..a5eb330f2 100644 --- a/src/hooks/wallet/useGrantSessionKey.ts +++ b/src/hooks/wallet/useGrantSessionKey.ts @@ -153,7 +153,14 @@ export const useGrantSessionKey = (): GrantSessionKeyResult => { // produce a passkey prompt. posthog.capture(ANALYTICS_EVENTS.CARD_SESSION_KEY_PROMPTED) // Triggers the passkey prompt — this is the one-time install. + // `address` is forced to the user's actual wallet so the approval + // binds to the deployed kernel. Pre-2025-09-18 users sit at a + // legacy V0_0_2-derived address (migrated in place to V0_0_3); the + // natural counterfactual of `createKernelAccount({sudo: newValidator})` + // is a different, never-funded address. Forcing the address here makes + // the grant work for both legacy and post-migration users. const sessionKernelAccount = await createKernelAccount(peanutPublicClient, { + address: kernelClient.account!.address, entryPoint: getEntryPoint('0.7'), kernelVersion: KERNEL_V3_1, plugins: {