diff --git a/src/packages/v4/hooks/useProjectHasErc20Token.ts b/src/packages/v4/hooks/useProjectHasErc20Token.ts index a38b254f4a..4a62028232 100644 --- a/src/packages/v4/hooks/useProjectHasErc20Token.ts +++ b/src/packages/v4/hooks/useProjectHasErc20Token.ts @@ -1,8 +1,9 @@ -import { useReadJbTokensTokenOf } from 'juice-sdk-react' +import { useJBTokenContext } from 'juice-sdk-react' import { isZeroAddress } from 'utils/address' export const useProjectHasErc20Token = () => { - const { data: tokenAddress } = useReadJbTokensTokenOf() + const { token } = useJBTokenContext() + const tokenAddress = token?.data?.address return Boolean(tokenAddress && !isZeroAddress(tokenAddress)) } diff --git a/src/packages/v4/hooks/useV4IssueErc20TokenTx.ts b/src/packages/v4/hooks/useV4IssueErc20TokenTx.ts index aff75e3842..0edd5fbb6d 100644 --- a/src/packages/v4/hooks/useV4IssueErc20TokenTx.ts +++ b/src/packages/v4/hooks/useV4IssueErc20TokenTx.ts @@ -1,9 +1,11 @@ import { useCallback, useContext } from 'react' +import { waitForTransactionReceipt } from '@wagmi/core' import { TxHistoryContext } from 'contexts/Transaction/TxHistoryContext' import { useJBContractContext, useWriteJbControllerDeployErc20For } from 'juice-sdk-react' import { Address, zeroAddress } from 'viem' import { BaseTxOpts } from '../models/transactions' +import { wagmiConfig } from '../wagmiConfig' export function useV4IssueErc20TokenTx() { const { addTransaction } = useContext(TxHistoryContext) @@ -47,12 +49,12 @@ export function useV4IssueErc20TokenTx() { onTransactionPendingCallback(hash) addTransaction?.('Launch ERC20 Token', { hash }) - // const transactionReceipt: WaitForTransactionReceiptReturnType = await waitForTransactionReceipt( - // wagmiConfig, - // { - // hash, - // }, - // ) + await waitForTransactionReceipt( + wagmiConfig, + { + hash, + }, + ) onTransactionConfirmedCallback() } catch (e) { diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4DistributePayoutsModal.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4DistributePayoutsModal.tsx index b559162cc0..dac12a89df 100644 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4DistributePayoutsModal.tsx +++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4DistributePayoutsModal.tsx @@ -45,7 +45,7 @@ export default function V4DistributePayoutsModal({ const [loading, setLoading] = useState() const [distributionAmount, setDistributionAmount] = useState() - const { writeContractAsync: writeSendPayouts, data } = + const { writeContractAsync: writeSendPayouts } = useWriteJbMultiTerminalSendPayoutsOf() async function executeDistributePayoutsTx() { diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4ClaimTokensModal.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4ClaimTokensModal.tsx new file mode 100644 index 0000000000..00c03e335b --- /dev/null +++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4ClaimTokensModal.tsx @@ -0,0 +1,199 @@ +import { WarningOutlined } from '@ant-design/icons' +import { t, Trans } from '@lingui/macro' +import { waitForTransactionReceipt } from '@wagmi/core' +import { Descriptions, Form } from 'antd' +import InputAccessoryButton from 'components/buttons/InputAccessoryButton' +import EthereumAddress from 'components/EthereumAddress' +import FormattedNumberInput from 'components/inputs/FormattedNumberInput' +import TransactionModal from 'components/modals/TransactionModal' +import { TxHistoryContext } from 'contexts/Transaction/TxHistoryContext' +import useSymbolOfERC20 from 'hooks/ERC20/useSymbolOfERC20' +import { useWallet } from 'hooks/Wallet' +import { NativeTokenValue, useJBContractContext, useReadJbTokensCreditBalanceOf, useReadJbTokensTokenOf, useWriteJbControllerClaimTokensFor } from 'juice-sdk-react' +import { useProjectHasErc20Token } from 'packages/v4/hooks/useProjectHasErc20Token' +import { wagmiConfig } from 'packages/v4/wagmiConfig' +import { useContext, useLayoutEffect, useState } from 'react' +import { fromWad, parseWad } from 'utils/format/formatNumber' +import { emitErrorNotification } from 'utils/notifications' +import { tokenSymbolText } from 'utils/tokenSymbolText' +import { zeroAddress } from 'viem' + +export function V4ClaimTokensModal({ + open, + onCancel, + onConfirmed, +}: { + open?: boolean + onCancel?: VoidFunction + onConfirmed?: VoidFunction +}) { + const { projectId, contracts } = useJBContractContext() + const { addTransaction } = useContext(TxHistoryContext) + + const { data: tokenAddress } = useReadJbTokensTokenOf() + const { data: tokenSymbol } = useSymbolOfERC20(tokenAddress) + + const [loading, setLoading] = useState() + const [transactionPending, setTransactionPending] = useState() + const [claimAmount, setClaimAmount] = useState() + + const { userAddress } = useWallet() + + const { writeContractAsync: writeClaimTokens } = + useWriteJbControllerClaimTokensFor() + + const hasIssuedTokens = useProjectHasErc20Token() + + const { data: unclaimedBalance } = useReadJbTokensCreditBalanceOf({ + args: [userAddress ?? zeroAddress, projectId], + }) + + useLayoutEffect(() => { + setClaimAmount(fromWad(unclaimedBalance)) + }, [unclaimedBalance]) + + async function executeClaimTokensTx() { + if ( + !contracts.controller.data || + !claimAmount || + !userAddress || + !projectId + ) + return + + setLoading(true) + + const args = [ + userAddress, + projectId, + parseWad(claimAmount).toBigInt(), + userAddress + ] as const + + try { + // SIMULATE TX: + // const encodedData = encodeFunctionData({ + // abi: jbControllerAbi, + // functionName: 'claimTokensFor', + // args, + // }) + // console.log('encodedData:', encodedData) + // console.log('contract:', contracts.controller.data) + + const hash = await writeClaimTokens({ + address: contracts.controller.data, + args, + }) + setTransactionPending(true) + + addTransaction?.('Claim tokens as ERC20', { hash }) + await waitForTransactionReceipt(wagmiConfig, { + hash, + }) + + setLoading(false) + setTransactionPending(false) + onConfirmed?.() + } catch (e) { + setLoading(false) + + emitErrorNotification((e as unknown as Error).message) + } + } + + const tokenTextLong = tokenSymbolText({ + tokenSymbol, + plural: true, + includeTokenWord: true, + }) + + const tokenTextShort = tokenSymbolText({ + tokenSymbol, + plural: true, + }) + + return ( + +
+ {!hasIssuedTokens && ( +
+ {' '} + + Tokens cannot be claimed because the project owner has not issued + an ERC-20 for this project. + +
+ )} + +
+

+ + Claiming {tokenTextLong} will convert your {tokenTextShort}{' '} + balance to ERC-20 tokens and mint them to your wallet. + +

+

+ + If you're not sure if you need to claim, you probably don't. + +

+

+ + You can redeem your {tokenTextLong} for ETH without claiming them. + You can transfer your unclaimed {tokenTextLong} to another address + from the Tools menu, which can be accessed from the wrench icon in + the upper right-hand corner of this project. + +

+
+ + + Your unclaimed {tokenTextLong}} + > + + + + {hasIssuedTokens && tokenSymbol && ( + {tokenSymbol} ERC-20 address} + > + + + )} + + +
+ + setClaimAmount(fromWad(unclaimedBalance))} + /> + } + onChange={val => setClaimAmount(val)} + /> + +
+
+
+ ) +} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4TokensPanel.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4TokensPanel.tsx index a302700ae0..8a3944fd87 100644 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4TokensPanel.tsx +++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4TokensPanel.tsx @@ -15,9 +15,11 @@ import { useJBContractContext } from 'juice-sdk-react' import { V4TokenHoldersModal } from 'packages/v4/components/modals/V4TokenHoldersModal/V4TokenHoldersModal' import { v4ProjectRoute } from 'packages/v4/utils/routes' import { useCallback, useState } from 'react' +import { reloadWindow } from 'utils/windowUtils' import { useChainId } from 'wagmi' import { useV4TokensPanel } from './hooks/useV4TokensPanel' import { useV4YourBalanceMenuItems } from './hooks/useV4YourBalanceMenuItems' +import { V4ClaimTokensModal } from './V4ClaimTokensModal' import { V4ReservedTokensSubPanel } from './V4ReservedTokensSubPanel' export const V4TokensPanel = () => { @@ -45,7 +47,7 @@ export const V4TokensPanel = () => { items, // redeemModalVisible, // setRedeemModalVisible, - // claimTokensModalVisible, + claimTokensModalVisible, setClaimTokensModalVisible, // mintModalVisible, // setMintModalVisible, @@ -68,7 +70,7 @@ export const V4TokensPanel = () => { title={t`Your balance`} description={ - {userTokenBalance.format()} tokens + {userTokenBalance.format(8)} tokens
{/* {projectHasErc20Token && (