Skip to content

Commit 2069a17

Browse files
committed
feat: chain selector in send payouts modal
1 parent 3ab9adc commit 2069a17

File tree

12 files changed

+205
-49
lines changed

12 files changed

+205
-49
lines changed

src/packages/v4/components/ChainSelect.tsx

+20-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import { JBChainId, SuckerPair } from 'juice-sdk-core'
2+
13
import { JuiceListbox } from 'components/inputs/JuiceListbox'
24
import { NETWORKS } from 'constants/networks'
3-
import { JBChainId, SuckerPair } from 'juice-sdk-core'
45
import { ChainLogo } from './ChainLogo'
56

67
function ChainSelectOption({
@@ -22,10 +23,12 @@ export const ChainSelect = ({
2223
value,
2324
onChange,
2425
suckers,
26+
showSelectedName
2527
}: {
2628
value: JBChainId | undefined
2729
onChange: (chainId: JBChainId) => void
2830
suckers: SuckerPair[]
31+
showSelectedName?: boolean // Otherwise just shows logo of selected chain
2932
}) => {
3033
const allowedChainIds = new Set(suckers?.map(sucker => sucker.peerChainId))
3134
const networkOptions = Object.entries(NETWORKS)
@@ -40,16 +43,30 @@ export const ChainSelect = ({
4043
value: parseInt(chainId) as JBChainId,
4144
}))
4245

46+
47+
let _valueLabel = <></>
48+
49+
if (value) {
50+
_valueLabel = showSelectedName ? (
51+
<div className="flex justify-center items-center gap-2">
52+
<ChainLogo chainId={value} />
53+
{NETWORKS[value]?.label}
54+
</div>
55+
) : <ChainLogo chainId={value} />
56+
}
57+
4358
const _value = {
4459
value,
45-
label: value ? <ChainLogo chainId={value} /> : 'Select chain',
60+
label: value ? _valueLabel : 'Select chain',
4661
}
4762

4863
return (
4964
<JuiceListbox
5065
value={_value}
5166
onChange={({ value: selectedChainId }) => {
52-
onChange(selectedChainId as JBChainId)
67+
if (selectedChainId) {
68+
onChange(selectedChainId)
69+
}
5370
}}
5471
options={networkOptions}
5572
buttonClassName="w-18"

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

+6-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,12 @@ export const usePayProjectTx = ({
161161
try {
162162
const hash = await writePay({
163163
chainId,
164-
address: contracts.primaryNativeTerminal.data,
164+
address: contracts.primaryNativeTerminal.data,
165+
// v4TODO Q: shouldnt above be:
166+
// useReadJbDirectoryPrimaryTerminalOf({
167+
// chainId: selectedChainId,
168+
// args: [projectId, NATIVE_TOKEN],
169+
// }) ???
165170
args,
166171
value: weiAmount,
167172
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import * as constants from '@ethersproject/constants'
2+
3+
import {
4+
JBChainId,
5+
useJBContractContext,
6+
useJBRuleset,
7+
useReadJbFundAccessLimitsPayoutLimitsOf,
8+
} from 'juice-sdk-react'
9+
10+
import { NATIVE_TOKEN } from 'juice-sdk-core'
11+
import { V4CurrencyOption } from '../models/v4CurrencyOption'
12+
import { V4_CURRENCY_ETH } from '../utils/currency'
13+
14+
/**
15+
* V4todo: add to SDK
16+
*/
17+
export function usePayoutLimitOfChain({
18+
chainId,
19+
projectId,
20+
}: {
21+
chainId: JBChainId | undefined,
22+
projectId: bigint | undefined
23+
}) {
24+
const {
25+
contracts: { primaryNativeTerminal, fundAccessLimits },
26+
} = useJBContractContext()
27+
const { data: ruleset } = useJBRuleset()
28+
const { data: payoutLimits, isLoading } =
29+
useReadJbFundAccessLimitsPayoutLimitsOf({
30+
address: fundAccessLimits.data ?? undefined,
31+
chainId,
32+
args: [
33+
projectId ?? 0n,
34+
BigInt(ruleset?.id ?? 0),
35+
primaryNativeTerminal.data ?? constants.AddressZero, // v4TODO: should be below?
36+
// useReadJbDirectoryPrimaryTerminalOf({
37+
// chainId,
38+
// args: [projectId, NATIVE_TOKEN],
39+
// })
40+
NATIVE_TOKEN,
41+
],
42+
})
43+
44+
const payoutLimit = payoutLimits?.[0]
45+
46+
return {
47+
data: payoutLimit
48+
? {
49+
...payoutLimit,
50+
currency: Number(payoutLimit.currency) as V4CurrencyOption,
51+
}
52+
: {
53+
amount: 0n,
54+
currency: V4_CURRENCY_ETH,
55+
},
56+
isLoading,
57+
}
58+
}

src/packages/v4/hooks/useUsedPayoutLimitOf.ts

+17-4
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,36 @@
11
import { NATIVE_CURRENCY_ID, NATIVE_TOKEN } from 'juice-sdk-core'
22
import {
3+
JBChainId,
34
useJBContractContext,
45
useJBRulesetContext,
56
useJBTerminalContext,
67
useReadJbTerminalStoreUsedPayoutLimitOf,
78
} from 'juice-sdk-react'
9+
810
import { zeroAddress } from 'viem'
911

10-
export const useUsedPayoutLimitOf = () => {
12+
export const useUsedPayoutLimitOf = ({
13+
chainId,
14+
projectId,
15+
}: {
16+
chainId: JBChainId | undefined,
17+
projectId: bigint | undefined
18+
}) => {
1119
const { store } = useJBTerminalContext()
12-
const { projectId, contracts } = useJBContractContext()
20+
const { contracts } = useJBContractContext()
1321
const { ruleset } = useJBRulesetContext()
1422

1523
const { data: usedPayoutLimit, isLoading } =
1624
useReadJbTerminalStoreUsedPayoutLimitOf({
1725
address: store.data ?? undefined,
26+
chainId,
1827
args: [
19-
contracts.primaryNativeTerminal.data ?? zeroAddress,
20-
projectId,
28+
contracts.primaryNativeTerminal.data ?? zeroAddress, // v4TODO: should be below?
29+
// useReadJbDirectoryPrimaryTerminalOf({
30+
// chainId: selectedChainId,
31+
// args: [projectId, NATIVE_TOKEN],
32+
// })
33+
projectId ?? 0n,
2134
NATIVE_TOKEN,
2235
BigInt(ruleset.data?.cycleNumber ?? 0),
2336
BigInt(NATIVE_CURRENCY_ID),

src/packages/v4/hooks/useV4BalanceOfNativeTerminal.ts

+12-5
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
1-
import { NATIVE_TOKEN } from 'juice-sdk-core';
21
import {
2+
JBChainId,
33
useJBContractContext,
44
useJBTerminalContext,
55
useReadJbTerminalStoreBalanceOf,
66
} from 'juice-sdk-react';
7+
8+
import { NATIVE_TOKEN } from 'juice-sdk-core';
79
import { zeroAddress } from 'viem';
810

9-
export const useV4BalanceOfNativeTerminal = () => {
11+
export const useV4BalanceOfNativeTerminal = ({ chainId, projectId }: { chainId: JBChainId | undefined, projectId: bigint | undefined }) => {
1012
const { store } = useJBTerminalContext();
11-
const { projectId, contracts } = useJBContractContext();
13+
const { contracts } = useJBContractContext();
1214

1315
const { data: treasuryBalance, isLoading } = useReadJbTerminalStoreBalanceOf({
1416
address: store.data ?? undefined,
17+
chainId,
1518
args: [
16-
contracts.primaryNativeTerminal.data ?? zeroAddress,
17-
projectId,
19+
contracts.primaryNativeTerminal.data ?? zeroAddress, // is this right, or should be below?
20+
// useReadJbDirectoryPrimaryTerminalOf({
21+
// chainId,
22+
// args: [projectId, NATIVE_TOKEN],
23+
// })
24+
projectId ?? 0n,
1825
NATIVE_TOKEN,
1926
],
2027
});

src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4CurrentUpcomingSubPanel.tsx

+12-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { InformationCircleIcon } from '@heroicons/react/24/outline'
44
import { currentCycleRemainingLengthTooltip } from 'components/Project/ProjectTabs/CyclesPayoutsTab/CyclesPanelTooltips'
55
import { UpcomingCycleChangesCallout } from 'components/Project/ProjectTabs/CyclesPayoutsTab/UpcomingCycleChangesCallout'
66
import { TitleDescriptionDisplayCard } from 'components/Project/ProjectTabs/TitleDescriptionDisplayCard'
7-
import { ProjectChainSelect } from 'packages/v4/components/ProjectDashboard/ProjectChainSelect'
7+
import { useSuckers } from 'juice-sdk-react'
8+
import { ChainSelect } from 'packages/v4/components/ChainSelect'
89
import { RulesetCountdownProvider } from 'packages/v4/contexts/RulesetCountdownProvider'
910
import { useState } from 'react'
1011
import { twMerge } from 'tailwind-merge'
@@ -38,6 +39,8 @@ export const V4CurrentUpcomingSubPanel = ({
3839
const { selectedChainId, setSelectedChainId } = useCyclesPanelSelectedChain()
3940
// const { data: rulesetsDiffAcrossChains } = useProjectRulesetsDiffAcrossChains({ rulesetNumber: info.rulesetNumber} )
4041

42+
const { data: suckers} = useSuckers()
43+
4144
const rulesetLengthTooltip =
4245
info.type === 'current' ? currentCycleRemainingLengthTooltip : undefined
4346

@@ -95,10 +98,14 @@ export const V4CurrentUpcomingSubPanel = ({
9598
<div className="absolute left-44 top-[-6px]">
9699
{ selectedChainId ?
97100
<div className="flex items-center gap-1">
98-
<ProjectChainSelect
99-
value={selectedChainId}
100-
onChange={(chainId) => setSelectedChainId(chainId)}
101-
/>
101+
{suckers && suckers.length > 1 ? (
102+
<ChainSelect
103+
value={selectedChainId}
104+
onChange={(chainId) => setSelectedChainId(chainId)}
105+
suckers={suckers}
106+
showSelectedName
107+
/>
108+
): null}
102109
{/* { rulesetsDiffAcrossChains?.length ?
103110
<Tooltip
104111
title={

src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4DistributePayoutsModal.tsx

+41-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { Trans, t } from '@lingui/macro'
22
import { NATIVE_TOKEN, NATIVE_TOKEN_DECIMALS } from 'juice-sdk-core'
33
import {
4+
JBChainId,
45
useJBContractContext,
6+
useReadJbDirectoryPrimaryTerminalOf,
7+
useSuckers,
58
useWriteJbMultiTerminalSendPayoutsOf,
69
} from 'juice-sdk-react'
710
import { V4CurrencyName, V4_CURRENCY_ETH } from 'packages/v4/utils/currency'
@@ -14,7 +17,9 @@ import { Callout } from 'components/Callout/Callout'
1417
import FormattedNumberInput from 'components/inputs/FormattedNumberInput'
1518
import TransactionModal from 'components/modals/TransactionModal'
1619
import { FEES_EXPLANATION } from 'components/strings'
20+
import { NETWORKS } from 'constants/networks'
1721
import { TxHistoryContext } from 'contexts/Transaction/TxHistoryContext'
22+
import { ChainSelect } from 'packages/v4/components/ChainSelect'
1823
import { PayoutsTable } from 'packages/v4/components/PayoutsTable/PayoutsTable'
1924
import { usePayoutLimit } from 'packages/v4/hooks/usePayoutLimit'
2025
import { useV4CurrentPayoutSplits } from 'packages/v4/hooks/useV4CurrentPayoutSplits'
@@ -34,24 +39,39 @@ export default function V4DistributePayoutsModal({
3439
}) {
3540
const { data: payoutSplits } = useV4CurrentPayoutSplits()
3641
const { data: payoutLimit } = usePayoutLimit()
37-
const { distributableAmount: distributable } = useV4DistributableAmount()
38-
const { contracts, projectId } = useJBContractContext()
42+
const { projectId } = useJBContractContext()
3943
const { addTransaction } = useContext(TxHistoryContext)
4044

45+
const { data: suckers } = useSuckers()
46+
4147
const payoutLimitAmountCurrency = payoutLimit?.currency ?? V4_CURRENCY_ETH
4248

4349
const [transactionPending, setTransactionPending] = useState<boolean>()
4450
const [loading, setLoading] = useState<boolean>()
4551
const [distributionAmount, setDistributionAmount] = useState<string>()
52+
const [selectedChainId, setSelectedChainId] = useState<JBChainId>()
4653

4754
const { writeContractAsync: writeSendPayouts } =
4855
useWriteJbMultiTerminalSendPayoutsOf()
49-
56+
57+
const { data: terminalAddress } = useReadJbDirectoryPrimaryTerminalOf({
58+
chainId: selectedChainId,
59+
args: [projectId, NATIVE_TOKEN],
60+
})
61+
62+
const selectedChainProjectId = suckers?.find((sucker) => sucker.peerChainId === selectedChainId)?.projectId
63+
64+
const { distributableAmount: distributable } = useV4DistributableAmount({
65+
chainId: selectedChainId,
66+
projectId: selectedChainProjectId
67+
})
68+
5069
async function executeDistributePayoutsTx() {
5170
if (
5271
!payoutLimitAmountCurrency ||
5372
!distributionAmount ||
54-
!contracts.primaryNativeTerminal.data ||
73+
!selectedChainId ||
74+
!terminalAddress ||
5575
!projectId
5676
)
5777
return
@@ -63,17 +83,18 @@ export default function V4DistributePayoutsModal({
6383
NATIVE_TOKEN,
6484
parseUnits(distributionAmount, NATIVE_TOKEN_DECIMALS),
6585
BigInt(payoutLimitAmountCurrency),
66-
0n, // TODO?
86+
0n, // minTokensPaidOut
6787
] as const
6888

6989
try {
7090
const hash = await writeSendPayouts({
71-
address: contracts.primaryNativeTerminal.data,
91+
address: terminalAddress, // TODOv4 Q: or should this just be contracts.primaryNativeTerminal.address?
92+
chainId: selectedChainId,
7293
args,
7394
})
7495
setTransactionPending(true)
7596

76-
addTransaction?.('Send payouts', { hash })
97+
addTransaction?.(`Send payouts on ${NETWORKS[selectedChainId]?.label}`, { hash })
7798
await waitForTransactionReceipt(wagmiConfig, {
7899
hash,
79100
})
@@ -111,6 +132,19 @@ export default function V4DistributePayoutsModal({
111132
>
112133
<div className="flex flex-col gap-6">
113134
<Form layout="vertical">
135+
{suckers && suckers.length > 1 ?
136+
<Form.Item
137+
className="mb-0"
138+
label={<Trans>Chain</Trans>}
139+
>
140+
<ChainSelect
141+
value={selectedChainId}
142+
onChange={setSelectedChainId}
143+
suckers={suckers}
144+
showSelectedName
145+
/>
146+
</Form.Item>
147+
: null }
114148
<Form.Item
115149
className="mb-0"
116150
label={<Trans>Amount to pay out</Trans>}

src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4SendPayoutsButton.tsx

+10-9
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
import { Button, Tooltip } from 'antd'
2+
import { useCallback, useState } from 'react'
3+
14
import { ArrowUpCircleIcon } from '@heroicons/react/24/outline'
25
import { Trans } from '@lingui/macro'
3-
import { Button, Tooltip } from 'antd'
4-
import { useCallback, useMemo, useState } from 'react'
56
import { twMerge } from 'tailwind-merge'
6-
import { useV4DistributableAmount } from './hooks/useV4DistributableAmount'
77
import V4DistributePayoutsModal from './V4DistributePayoutsModal'
88

99
export const V4SendPayoutsButton = ({
@@ -13,26 +13,27 @@ export const V4SendPayoutsButton = ({
1313
className?: string
1414
containerClassName?: string
1515
}) => {
16-
const { distributableAmount } = useV4DistributableAmount()
16+
// const { distributableAmount } = useV4DistributableAmount()
1717
const [open, setOpen] = useState(false)
1818
const openModal = useCallback(() => setOpen(true), [])
1919
const closeModal = useCallback(() => setOpen(false), [])
2020

21-
const distributeButtonDisabled = useMemo(() => {
22-
return distributableAmount.value === 0n
23-
}, [distributableAmount])
21+
// v4TODO: aggregate distributable amount
22+
// const distributeButtonDisabled = useMemo(() => {
23+
// return distributableAmount.value === 0n
24+
// }, [distributableAmount])
2425

2526
return (
2627
<Tooltip
2728
title={<Trans>No payouts remaining for this cycle.</Trans>}
28-
open={distributeButtonDisabled ? undefined : false}
29+
// open={distributeButtonDisabled ? undefined : false}
2930
className={containerClassName}
3031
>
3132
<Button
3233
type="primary"
3334
className={twMerge('flex w-fit items-center gap-3', className)}
3435
onClick={openModal}
35-
disabled={distributeButtonDisabled}
36+
// disabled={distributeButtonDisabled}
3637
>
3738
<Trans>Send payouts</Trans>
3839
<ArrowUpCircleIcon className="h-5 w-5" />

0 commit comments

Comments
 (0)