Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
61a21b4
add daimo pay
Zishan-7 Aug 18, 2025
669af30
minor improvements
Zishan-7 Aug 18, 2025
8746b07
cleanup and add success state
Zishan-7 Aug 18, 2025
b1f31bf
resolve dependency issues
Zishan-7 Aug 19, 2025
77a5d03
fix: formatting
Zishan-7 Aug 20, 2025
e5297b3
fix: recent methods redirection
Zishan-7 Aug 20, 2025
506ec1d
add functions for daimo payment in request fulfilment flow
Zishan-7 Aug 20, 2025
fc2322e
Integrate daimo in request fulfilment flow
Zishan-7 Aug 20, 2025
1047c84
remove hardcoded address
Zishan-7 Aug 20, 2025
4c8a87b
add separate arbitrum usdc flow for deposits
Zishan-7 Aug 21, 2025
7fe6042
Add risk modal
Zishan-7 Aug 21, 2025
feeaea8
fix overlay blur
Zishan-7 Aug 21, 2025
0129b72
Enhance loading state indication in payment process
Zishan-7 Aug 21, 2025
213d6ef
Add payer's address in deposit history entry
Zishan-7 Aug 21, 2025
6c14b59
Add validation
Zishan-7 Aug 22, 2025
8cebbaf
add error handling
Zishan-7 Aug 22, 2025
281cf66
remove action and move logic to API route
Zishan-7 Aug 22, 2025
ba99ea8
Merge remote-tracking branch 'origin/peanut-wallet-dev' into feat/int…
Zishan-7 Aug 22, 2025
a4e3b2a
fix errors
Zishan-7 Aug 22, 2025
0114c2d
fix: request flow
Zishan-7 Aug 22, 2025
1e72702
fix: validation
Zishan-7 Aug 22, 2025
563bc88
fixes
Zishan-7 Aug 22, 2025
31557c2
Merge remote-tracking branch 'origin/peanut-wallet-dev' into feat/int…
Zishan-7 Aug 22, 2025
0b3f720
add daimo flow in country specific method
Zishan-7 Aug 22, 2025
5cdad60
fix: slider not working on first attempt
Zishan-7 Aug 22, 2025
1f36903
filter supported networks
Zishan-7 Aug 24, 2025
ecef7a9
create reusable daimo button
Zishan-7 Aug 24, 2025
d4939b7
remove space
Zishan-7 Aug 24, 2025
60b650b
remove route.ts file and move logic to server actions
Zishan-7 Aug 25, 2025
92e9063
fix: infinite loading edge case
Zishan-7 Aug 25, 2025
6741c9f
update api and remove delay
Zishan-7 Aug 25, 2025
c3e6a08
fix: layout shift
Zishan-7 Aug 25, 2025
73ad4e8
fix: shadow
Zishan-7 Aug 25, 2025
7a21e90
update function name
Zishan-7 Aug 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export NEXT_PUBLIC_BALANCE_WARNING_THRESHOLD=1
export NEXT_PUBLIC_BALANCE_WARNING_EXPIRY=15

export NEXT_PUBLIC_INFURA_API_KEY=""
export NEXT_PUBLIC_DAIMO_APP_ID=""

export BRIDGE_API_KEY=""
export PROMO_LIST={}
Expand Down
14 changes: 8 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@chakra-ui/react": "^2.10.4",
"@chakra-ui/react-context": "^2.1.0",
"@chakra-ui/shared-utils": "^2.0.4",
"@daimo/pay": "^1.15.0",
"@dicebear/collection": "^9.2.2",
"@dicebear/core": "^9.2.2",
"@headlessui/react": "^1.7.19",
Expand All @@ -45,7 +46,7 @@
"@tanstack/react-query": "5.8.4",
"@typeform/embed-react": "^3.20.0",
"@vercel/analytics": "^1.4.1",
"@wagmi/core": "2.14.3",
"@wagmi/core": "2.19.0",
"@zerodev/passkey-validator": "^5.5.0",
"@zerodev/sdk": "5.4.0",
"auto-text-size": "^0.2.3",
Expand Down Expand Up @@ -78,8 +79,8 @@
"uuid": "^10.0.0",
"validator": "^13.12.0",
"vaul": "^1.1.2",
"viem": "^2.21.48",
"wagmi": "2.8.6",
"viem": "^2.22.0",
"wagmi": "2.16.3",
"web-push": "^3.6.7",
"yup": "^1.4.0"
},
Expand Down Expand Up @@ -153,9 +154,10 @@
]
},
"resolutions": {
"@wagmi/core": "2.14.3",
"viem": "^2.21.48",
"wagmi": "2.8.6"
"@wagmi/core": "2.19.0",
"viem": "^2.22.0",
"wagmi": "2.16.3",
"ox": "0.6.5"
},
"size-limit": [
{
Expand Down
9,808 changes: 4,848 additions & 4,960 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions public/game/peanut-game.html
Original file line number Diff line number Diff line change
Expand Up @@ -1001,7 +1001,7 @@
* Maximum obstacle grouping count.
* @const
*/
;(Obstacle.MAX_OBSTACLE_LENGTH = 3),
;((Obstacle.MAX_OBSTACLE_LENGTH = 3),
(Obstacle.prototype = {
/**
* Initialise the DOM for the obstacle.
Expand Down Expand Up @@ -1103,7 +1103,7 @@
)
}
},
})
}))
/**
* Obstacle definitions.
* minGap: minimum pixel space betweeen obstacles.
Expand Down
113 changes: 87 additions & 26 deletions src/app/(mobile-ui)/add-money/crypto/direct/page.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,100 @@
'use client'

import PaymentPage from '@/app/[...recipient]/client'
import ErrorAlert from '@/components/Global/ErrorAlert'
import NavHeader from '@/components/Global/NavHeader'
import PeanutLoading from '@/components/Global/PeanutLoading'
import { useAppDispatch, useUserStore } from '@/redux/hooks'
import { paymentActions } from '@/redux/slices/payment-slice'
import { useRouter, useSearchParams } from 'next/navigation'
import { useEffect, useState } from 'react'
import TokenAmountInput from '@/components/Global/TokenAmountInput'
import DaimoPayButton from '@/components/Global/DaimoPayButton'
import DirectSuccessView from '@/components/Payment/Views/Status.payment.view'
import { useWallet } from '@/hooks/wallet/useWallet'
import { useRouter } from 'next/navigation'
import { useState } from 'react'
import { trackDaimoDepositTransactionHash } from '@/app/actions/users'

export default function AddMoneyCryptoDirectPage() {
const router = useRouter()
const searchParams = useSearchParams()
const { user } = useUserStore()
const [recipientUsername, setRecipientUsername] = useState<string | null>(null)
const [isLoading, setIsLoading] = useState(true)
const dispatch = useAppDispatch()

useEffect(() => {
dispatch(paymentActions.resetPaymentState())
}, [dispatch])

useEffect(() => {
if (user?.user.username) {
setRecipientUsername(user.user.username)
} else {
router.replace('/add-money/crypto')
return
const { address } = useWallet()
const [inputTokenAmount, setInputTokenAmount] = useState<string>('')
const [isPaymentSuccess, setisPaymentSuccess] = useState(false)
const [isUpdatingDepositStatus, setIsUpdatingDepositStatus] = useState(false)
const [error, setError] = useState<string | null>(null)

const onPaymentCompleted = async (e: any) => {
setIsUpdatingDepositStatus(true)

// Save deposit txn hash in the backend to track the user's deposit
try {
await trackDaimoDepositTransactionHash(e.txHash, e.payment.source.payerAddress)
} catch (error) {
console.error('Error updating depositor address:', error)
} finally {
setIsUpdatingDepositStatus(false)
setisPaymentSuccess(true)
}
setIsLoading(false)
}, [searchParams, router, user])
}

if (isLoading) {
if (isUpdatingDepositStatus) {
return <PeanutLoading />
}

const recipientPathSegments = [recipientUsername ?? '']
if (isPaymentSuccess) {
return (
<DirectSuccessView
key={`success-add-money}`}
headerTitle={'Add Money'}
type="SEND"
currencyAmount={`$${inputTokenAmount}`}
isWithdrawFlow={false}
redirectTo={'/add-money'}
/>
)
}

return (
<div className="flex min-h-[inherit] flex-col justify-between gap-8">
<NavHeader
onPrev={() => {
if (window.history.length > 1) {
router.back()
} else {
router.push('/')
}
}}
title={'Add Money'}
/>
<div className="my-auto flex h-full flex-col justify-center space-y-4">
<div className="text-sm font-bold">How much do you want to add?</div>
<TokenAmountInput
tokenValue={inputTokenAmount}
setTokenValue={(value: string | undefined) => setInputTokenAmount(value || '')}
className="w-full"
currency={{
code: 'USD',
symbol: '$',
price: 1,
}}
hideCurrencyToggle
hideBalance
/>

{address && (
<DaimoPayButton
amount={inputTokenAmount}
toAddress={address}
onPaymentCompleted={onPaymentCompleted}
variant="purple"
icon="plus"
iconSize={16}
minAmount={0.1}
maxAmount={4000}
onValidationError={setError}
>
Add Money
</DaimoPayButton>
)}

return <PaymentPage recipient={recipientPathSegments} flow="external_wallet" />
<div className="min-h-[20px]">{error && <ErrorAlert description={error} />}</div>
</div>
</div>
)
}
48 changes: 28 additions & 20 deletions src/app/(mobile-ui)/add-money/crypto/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
'use client'
import { ARBITRUM_ICON } from '@/assets'
import { CryptoSourceListCard } from '@/components/AddMoney/components/CryptoSourceListCard'
import { CRYPTO_EXCHANGES, CRYPTO_WALLETS, CryptoSource, CryptoToken } from '@/components/AddMoney/consts'
import {
CRYPTO_EXCHANGES,
CRYPTO_WALLETS,
CryptoSource,
CryptoToken,
DEPOSIT_CRYPTO_TOKENS,
} from '@/components/AddMoney/consts'
import { CryptoDepositQR } from '@/components/AddMoney/views/CryptoDepositQR.view'
import NetworkSelectionView, { SelectedNetwork } from '@/components/AddMoney/views/NetworkSelection.view'
import TokenSelectionView from '@/components/AddMoney/views/TokenSelection.view'
import ActionModal from '@/components/Global/ActionModal'
import NavHeader from '@/components/Global/NavHeader'
import PeanutLoading from '@/components/Global/PeanutLoading'
import { Slider } from '@/components/Slider'
import { PEANUT_WALLET_CHAIN } from '@/constants'
import { useWallet } from '@/hooks/wallet/useWallet'
import { useRouter } from 'next/navigation'
import { useEffect, useState } from 'react'
Expand All @@ -21,11 +30,15 @@ interface AddMoneyCryptoPageProps {

const AddMoneyCryptoPage = ({ headerTitle, onBack, depositAddress }: AddMoneyCryptoPageProps) => {
const router = useRouter()
const { address: peanutWalletAddress } = useWallet()
const [currentStep, setCurrentStep] = useState<AddMoneyCryptoStep>('tokenSelection') // hotfix for deposit - select tokenSelection view as default
const [selectedSource, setSelectedSource] = useState<CryptoSource | null>(CRYPTO_EXCHANGES[3]) // hotfix for deposit - select Other exhange by default
const [selectedToken, setSelectedToken] = useState<CryptoToken | null>(null)
const [selectedNetwork, setSelectedNetwork] = useState<SelectedNetwork | null>(null)
const { address: peanutWalletAddress, isConnected } = useWallet()
const [currentStep, setCurrentStep] = useState<AddMoneyCryptoStep>('qrScreen')
const [selectedSource, setSelectedSource] = useState<CryptoSource | null>(CRYPTO_EXCHANGES[3])
const [selectedToken, setSelectedToken] = useState<CryptoToken | null>(DEPOSIT_CRYPTO_TOKENS[0])
const [selectedNetwork, setSelectedNetwork] = useState<SelectedNetwork | null>({
chainId: PEANUT_WALLET_CHAIN.id.toString(),
name: PEANUT_WALLET_CHAIN.name,
iconUrl: ARBITRUM_ICON,
})
const [isRiskAccepted, setIsRiskAccepted] = useState(false)

useEffect(() => {
Expand Down Expand Up @@ -57,11 +70,6 @@ const AddMoneyCryptoPage = ({ headerTitle, onBack, depositAddress }: AddMoneyCry
}

const handleBackToSourceSelection = () => {
// hotfix for deposit - redirect to previous route if user is on tokenSelection and selected source is other-exchanges
if (selectedSource?.id === 'other-exchanges' && currentStep === 'tokenSelection') {
router.back()
return
}
setCurrentStep('sourceSelection')
setSelectedSource(null)
resetSelections()
Expand All @@ -78,14 +86,6 @@ const AddMoneyCryptoPage = ({ headerTitle, onBack, depositAddress }: AddMoneyCry
setIsRiskAccepted(false)
}

const handleBackToNetworkSelectionFromQR = () => {
if (selectedSource?.type === 'exchange') {
setCurrentStep('tokenSelection') // hotfix for deposit - redirect to tokenSelection view if user is on qrScreen and selected source is exchange
} else {
setCurrentStep('networkSelection')
}
}

if (currentStep === 'tokenSelection' && selectedSource) {
return (
<TokenSelectionView
Expand Down Expand Up @@ -130,12 +130,20 @@ const AddMoneyCryptoPage = ({ headerTitle, onBack, depositAddress }: AddMoneyCry
}

if (currentStep === 'qrScreen' && selectedSource && selectedToken && selectedNetwork) {
if (!isConnected) {
return <PeanutLoading />
}

if (isConnected && !peanutWalletAddress) {
router.push('/')
return null
}
return (
<CryptoDepositQR
tokenName={selectedToken.symbol}
chainName={selectedNetwork.name}
depositAddress={depositAddress ?? peanutWalletAddress}
onBack={handleBackToNetworkSelectionFromQR}
onBack={() => router.back()}
/>
)
}
Expand Down
32 changes: 32 additions & 0 deletions src/app/actions/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,35 @@ export async function getUserById(userId: string): Promise<User | null> {
return null
}
}

export async function trackDaimoDepositTransactionHash(txHash: string, payerAddress: string): Promise<{ data?: any }> {
try {
if (!txHash || !payerAddress) {
throw new Error('Missing required fields: txHash and payerAddress')
}

const response = await fetchWithSentry(`${PEANUT_API_URL}/users/track-transaction`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'api-key': API_KEY,
},
body: JSON.stringify({
txHash,
payerAddress,
}),
})

const responseJson = await response.json()
if (!response.ok) {
throw new Error(
responseJson.message ||
responseJson.error ||
`Failed to save deposit address with status: ${response.status}`
)
}
return { data: responseJson }
} catch (e: any) {
throw new Error(e.message || e.toString() || 'An unexpected error occurred')
}
}
1 change: 1 addition & 0 deletions src/assets/chains/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export { default as ARBITRUM_ICON } from './arbitrum.svg'
export { default as MANTLE_ICON } from './mantle.svg'
export { default as SOLANA_ICON } from './solana.svg'
export { default as TRON_ICON } from './tron.svg'
export { default as OTHER_CHAINS_ICON } from './other-chains.svg'

13 changes: 13 additions & 0 deletions src/assets/chains/other-chains.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading