Skip to content

Commit 0bfe8e0

Browse files
authored
Apply v4 NFT credit on checkout (#4542)
1 parent 0322064 commit 0bfe8e0

File tree

10 files changed

+309
-89
lines changed

10 files changed

+309
-89
lines changed

src/locales/messages.pot

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,9 +1229,6 @@ msgstr ""
12291229
msgid "Issue as ERC-20"
12301230
msgstr ""
12311231

1232-
msgid "Pay {primaryAmount}"
1233-
msgstr ""
1234-
12351232
msgid "Active"
12361233
msgstr ""
12371234

src/packages/v4/components/ProjectDashboard/ReduxProjectCartProvider.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { useWallet } from 'hooks/Wallet'
2-
import { useReadJb721TiersHookPayCreditsOf } from 'juice-sdk-react'
31
import { useV4NftRewards } from 'packages/v4/contexts/V4NftRewards/V4NftRewardsProvider'
2+
import { useV4UserNftCredits } from 'packages/v4/contexts/V4UserNftCreditsProvider'
43
import { V4CurrencyOption } from 'packages/v4/models/v4CurrencyOption'
54
import React from 'react'
65
import { useProjectDispatch } from './redux/hooks'
@@ -25,12 +24,7 @@ export const ReduxProjectCartProvider = ({
2524
const {
2625
nftRewards: { rewardTiers },
2726
} = useV4NftRewards()
28-
29-
const { userAddress } = useWallet()
30-
const { data: nftCredits } = useReadJb721TiersHookPayCreditsOf({
31-
address: userAddress,
32-
})
33-
27+
const nftCredits = useV4UserNftCredits()
3428
const dispatch = useProjectDispatch()
3529

3630
// Set the nfts on load
@@ -40,8 +34,9 @@ export const ReduxProjectCartProvider = ({
4034

4135
// Set the user's NFT credits on load
4236
React.useEffect(() => {
43-
dispatch(projectCartActions.setUserNftCredits(nftCredits ?? 0n))
44-
}, [dispatch, nftCredits])
37+
if (nftCredits.isLoading) return
38+
dispatch(projectCartActions.setUserNftCredits(nftCredits.data ?? 0n))
39+
}, [dispatch, nftCredits.isLoading, nftCredits.data])
4540

4641
return <>{children}</>
4742
}

src/packages/v4/components/ProjectDashboard/V4PayRedeemCard/PayProjectModal/PayProjectModal.tsx

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import EtherscanLink from 'components/EtherscanLink'
33
import ExternalLink from 'components/ExternalLink'
44
import { JuiceModal } from 'components/modals/JuiceModal'
55
import { Formik } from 'formik'
6-
import Image from "next/legacy/image"
6+
import Image from 'next/legacy/image'
7+
import { useV4UserNftCredits } from 'packages/v4/contexts/V4UserNftCreditsProvider'
78
import { twMerge } from 'tailwind-merge'
89
import { helpPagePath } from 'utils/helpPagePath'
910
import { MessageSection } from './components/MessageSection'
1011
import { ReceiveSection } from './components/ReceiveSection'
12+
import { usePayAmounts } from './hooks/usePayAmounts'
1113
import {
1214
PayProjectModalFormValues,
1315
usePayProjectModal,
@@ -16,8 +18,6 @@ import {
1618
export const PayProjectModal: React.FC = () => {
1719
const {
1820
open,
19-
primaryAmount,
20-
secondaryAmount,
2121
validationSchema,
2222
isTransactionPending,
2323
isTransactionConfirmed,
@@ -27,6 +27,7 @@ export const PayProjectModal: React.FC = () => {
2727
setOpen,
2828
onPaySubmit,
2929
} = usePayProjectModal()
30+
const { formattedTotalAmount } = usePayAmounts()
3031

3132
return (
3233
<Formik<PayProjectModalFormValues>
@@ -50,7 +51,7 @@ export const PayProjectModal: React.FC = () => {
5051
position="top"
5152
okLoading={props.isSubmitting || isTransactionPending}
5253
okButtonForm="PayProjectModalForm"
53-
okText={t`Pay ${primaryAmount}`}
54+
okText={t`Pay ${formattedTotalAmount.primaryAmount}`}
5455
cancelText={
5556
isTransactionPending || isTransactionConfirmed
5657
? t`Close`
@@ -97,19 +98,7 @@ export const PayProjectModal: React.FC = () => {
9798
) : (
9899
<>
99100
<div className="flex flex-col divide-y divide-grey-200 dark:divide-slate-500">
100-
<div className="flex justify-between gap-3 py-3">
101-
<span className="font-medium">
102-
<Trans>Total amount</Trans>
103-
</span>
104-
<div>
105-
<span>{primaryAmount}</span>{' '}
106-
{secondaryAmount && (
107-
<span className="text-grey-500 dark:text-slate-200">
108-
({secondaryAmount})
109-
</span>
110-
)}
111-
</div>
112-
</div>
101+
<AmountSection />
113102

114103
<ReceiveSection className="py-6" />
115104

@@ -172,3 +161,60 @@ export const PayProjectModal: React.FC = () => {
172161
</Formik>
173162
)
174163
}
164+
165+
const AmountSection = () => {
166+
const { data: nftCredits } = useV4UserNftCredits()
167+
const { formattedAmount, formattedNftCredits, formattedTotalAmount } =
168+
usePayAmounts()
169+
170+
const RowData = ({
171+
label,
172+
primaryAmount,
173+
secondaryAmount,
174+
}: {
175+
label: React.ReactNode
176+
primaryAmount: React.ReactNode
177+
secondaryAmount: React.ReactNode
178+
}) => (
179+
<div className="flex justify-between gap-3 py-3">
180+
<span className="font-medium">{label}</span>
181+
<div>
182+
<span>{primaryAmount}</span>{' '}
183+
{secondaryAmount && (
184+
<span className="text-grey-500 dark:text-slate-200">
185+
({secondaryAmount})
186+
</span>
187+
)}
188+
</div>
189+
</div>
190+
)
191+
192+
if (!nftCredits || nftCredits <= 0n || !formattedNftCredits)
193+
return (
194+
<RowData
195+
label={t`Total amount`}
196+
primaryAmount={formattedTotalAmount?.primaryAmount}
197+
secondaryAmount={formattedTotalAmount?.secondaryAmount}
198+
/>
199+
)
200+
201+
return (
202+
<div>
203+
<RowData
204+
label={t`Amount`}
205+
primaryAmount={formattedAmount.primaryAmount}
206+
secondaryAmount={formattedAmount.secondaryAmount}
207+
/>
208+
<RowData
209+
label={t`NFT Credits`}
210+
primaryAmount={`-${formattedNftCredits.primaryAmount}`}
211+
secondaryAmount={`-${formattedNftCredits.secondaryAmount}`}
212+
/>
213+
<RowData
214+
label={t`Total amount`}
215+
primaryAmount={formattedTotalAmount?.primaryAmount}
216+
secondaryAmount={formattedTotalAmount?.secondaryAmount}
217+
/>
218+
</div>
219+
)
220+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import { useCurrencyConverter } from 'hooks/useCurrencyConverter'
2+
import { useV4UserNftCredits } from 'packages/v4/contexts/V4UserNftCreditsProvider'
3+
import { V4_CURRENCY_ETH, V4_CURRENCY_USD } from 'packages/v4/utils/currency'
4+
import { formatCurrencyAmount } from 'packages/v4/utils/formatCurrencyAmount'
5+
import React from 'react'
6+
import { fromWad, parseWad } from 'utils/format/formatNumber'
7+
import { useProjectSelector } from '../../../redux/hooks'
8+
import { usePayProjectModal } from './usePayProjectModal/usePayProjectModal'
9+
10+
export const usePayAmounts = () => {
11+
const converter = useCurrencyConverter()
12+
const { payAmount } = useProjectSelector(state => state.projectCart)
13+
const { primaryAmount, secondaryAmount } = usePayProjectModal()
14+
const { data: nftCreditsData } = useV4UserNftCredits()
15+
16+
const payAmountRaw = React.useMemo(() => {
17+
if (!payAmount) return
18+
19+
switch (payAmount.currency) {
20+
case V4_CURRENCY_ETH:
21+
return {
22+
eth: parseWad(payAmount.amount),
23+
usd: converter.weiToUsd(parseWad(payAmount.amount))!,
24+
}
25+
case V4_CURRENCY_USD:
26+
return {
27+
eth: converter.usdToWei(payAmount.amount),
28+
usd: parseWad(payAmount.amount),
29+
}
30+
}
31+
}, [converter, payAmount])
32+
33+
const appliedNFTCreditsRaw = React.useMemo(() => {
34+
if (!payAmountRaw || !nftCreditsData) return
35+
36+
const nftCreditsApplied = payAmountRaw.eth.lt(nftCreditsData)
37+
? payAmountRaw.eth
38+
: nftCreditsData
39+
40+
const eth = nftCreditsApplied
41+
const usd = parseWad(converter.weiToUsd(nftCreditsApplied))!
42+
43+
return {
44+
eth,
45+
usd,
46+
}
47+
}, [converter, nftCreditsData, payAmountRaw])
48+
49+
const formattedNftCredits = React.useMemo(() => {
50+
if (!appliedNFTCreditsRaw || !payAmount) return
51+
52+
switch (payAmount.currency) {
53+
case V4_CURRENCY_ETH:
54+
return {
55+
primaryAmount: formatCurrencyAmount({
56+
amount: fromWad(appliedNFTCreditsRaw.eth),
57+
currency: V4_CURRENCY_ETH,
58+
}),
59+
secondaryAmount: formatCurrencyAmount({
60+
amount: fromWad(appliedNFTCreditsRaw.usd),
61+
currency: V4_CURRENCY_USD,
62+
}),
63+
}
64+
case V4_CURRENCY_USD:
65+
return {
66+
primaryAmount: formatCurrencyAmount({
67+
amount: fromWad(appliedNFTCreditsRaw.usd),
68+
currency: V4_CURRENCY_USD,
69+
}),
70+
secondaryAmount: formatCurrencyAmount({
71+
amount: fromWad(appliedNFTCreditsRaw.eth),
72+
currency: V4_CURRENCY_ETH,
73+
}),
74+
}
75+
}
76+
}, [appliedNFTCreditsRaw, payAmount])
77+
78+
const formattedTotalAmount = React.useMemo(() => {
79+
if (!payAmountRaw || !payAmount) return
80+
81+
if (!appliedNFTCreditsRaw) {
82+
return {
83+
primaryAmount: primaryAmount,
84+
secondaryAmount: secondaryAmount,
85+
}
86+
}
87+
88+
const totalEth = payAmountRaw.eth.sub(appliedNFTCreditsRaw.eth)
89+
const totalUsd = converter.weiToUsd(parseWad(totalEth))
90+
91+
const formattedEth = formatCurrencyAmount({
92+
amount: fromWad(totalEth),
93+
currency: V4_CURRENCY_ETH,
94+
})
95+
const formattedUsd = formatCurrencyAmount({
96+
amount: fromWad(totalUsd),
97+
currency: V4_CURRENCY_USD,
98+
})
99+
100+
switch (payAmount?.currency) {
101+
case V4_CURRENCY_ETH:
102+
return {
103+
primaryAmount: formattedEth,
104+
secondaryAmount: formattedUsd,
105+
}
106+
case V4_CURRENCY_USD:
107+
return {
108+
primaryAmount: formattedUsd,
109+
secondaryAmount: formattedEth,
110+
}
111+
}
112+
}, [
113+
appliedNFTCreditsRaw,
114+
converter,
115+
payAmount,
116+
payAmountRaw,
117+
primaryAmount,
118+
secondaryAmount,
119+
])
120+
121+
return {
122+
formattedAmount: { primaryAmount, secondaryAmount },
123+
formattedNftCredits: {
124+
primaryAmount: formattedNftCredits?.primaryAmount,
125+
secondaryAmount: formattedNftCredits?.secondaryAmount,
126+
},
127+
formattedTotalAmount: {
128+
primaryAmount: formattedTotalAmount?.primaryAmount,
129+
secondaryAmount: formattedTotalAmount?.secondaryAmount,
130+
},
131+
}
132+
}

src/packages/v4/components/ProjectDashboard/V4PayRedeemCard/PayProjectModal/hooks/usePayProjectModal/usePayProjectTx.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
} from 'juice-sdk-react'
1313
import { useProjectSelector } from 'packages/v4/components/ProjectDashboard/redux/hooks'
1414
import { useV4NftRewards } from 'packages/v4/contexts/V4NftRewards/V4NftRewardsProvider'
15+
import { useV4UserNftCredits } from 'packages/v4/contexts/V4UserNftCreditsProvider'
1516
import { useProjectHasErc20Token } from 'packages/v4/hooks/useProjectHasErc20Token'
1617
import { V4_CURRENCY_ETH } from 'packages/v4/utils/currency'
1718
import { ProjectPayReceipt } from 'packages/v4/views/V4ProjectDashboard/hooks/useProjectPageQueries'
@@ -40,6 +41,7 @@ export const usePayProjectTx = ({
4041
) => void
4142
}) => {
4243
const { userAddress } = useWallet()
44+
const { data: nftCredits } = useV4UserNftCredits()
4345
const { payAmount, chosenNftRewards } = useProjectSelector(
4446
state => state.projectCart,
4547
)
@@ -71,12 +73,21 @@ export const usePayProjectTx = ({
7173
const weiAmount = useMemo(() => {
7274
if (!payAmount) {
7375
return 0n
74-
} else if (payAmount.currency === V4_CURRENCY_ETH) {
75-
return parseEther(payAmount.amount.toString())
76-
} else {
77-
return converter.usdToWei(payAmount.amount).toBigInt()
7876
}
79-
}, [payAmount, converter])
77+
let weiAmount =
78+
payAmount.currency === V4_CURRENCY_ETH
79+
? parseEther(payAmount.amount.toString())
80+
: converter.usdToWei(payAmount.amount).toBigInt()
81+
if (nftCredits) {
82+
if (nftCredits >= weiAmount) {
83+
weiAmount = 0n
84+
} else {
85+
weiAmount -= nftCredits
86+
}
87+
}
88+
89+
return weiAmount
90+
}, [converter, nftCredits, payAmount])
8091

8192
const {
8293
rulesetMetadata: { data: rulesetMetadata },
@@ -104,7 +115,6 @@ export const usePayProjectTx = ({
104115
formikHelpers: FormikHelpers<PayProjectModalFormValues>,
105116
) => {
106117
if (
107-
!weiAmount ||
108118
!contracts.primaryNativeTerminal.data ||
109119
!userAddress ||
110120
!values.userAcceptsTerms

src/packages/v4/components/ProjectDashboard/V4PayRedeemCard/V4NftCreditsCallouts.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
import { CubeIcon } from '@heroicons/react/24/outline'
22
import { Trans } from '@lingui/macro'
33
import { Button } from 'antd'
4-
import { useWallet } from 'hooks/Wallet'
54
import { formatEther } from 'juice-sdk-core'
6-
import { useReadJb721TiersHookPayCreditsOf } from 'juice-sdk-react'
5+
import { useV4UserNftCredits } from 'packages/v4/contexts/V4UserNftCreditsProvider'
76
import { useProjectPageQueries } from 'packages/v4/views/V4ProjectDashboard/hooks/useProjectPageQueries'
87

98
export function V4NftCreditsCallouts() {
109
const { setProjectPageTab } = useProjectPageQueries()
11-
const { userAddress } = useWallet()
12-
const { data: nftCredits } = useReadJb721TiersHookPayCreditsOf({
13-
address: userAddress,
14-
})
10+
const { data: nftCredits } = useV4UserNftCredits()
1511

1612
if (!nftCredits || nftCredits <= 0n) {
1713
return null

0 commit comments

Comments
 (0)