diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/SolanaRedpacketDialog/RedpacketConfirm.tsx b/packages/plugins/RedPacket/src/SiteAdaptor/SolanaRedpacketDialog/RedpacketConfirm.tsx index 57fb4e36aadb..98bc41a214c6 100644 --- a/packages/plugins/RedPacket/src/SiteAdaptor/SolanaRedpacketDialog/RedpacketConfirm.tsx +++ b/packages/plugins/RedPacket/src/SiteAdaptor/SolanaRedpacketDialog/RedpacketConfirm.tsx @@ -151,8 +151,8 @@ export function SolanaRedPacketConfirm() { DEFAULT_DURATION, !!isRandom, claimer, - message, creator, + message, ) : tokenMint ? createWithSplToken( @@ -163,8 +163,9 @@ export function SolanaRedPacketConfirm() { DEFAULT_DURATION, !!isRandom, claimer, - message, + creator, + message, ) : null) if (!result) return diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/SolanaRedpacketDialog/Routes.tsx b/packages/plugins/RedPacket/src/SiteAdaptor/SolanaRedpacketDialog/Routes.tsx index a4c2895448ad..4c7c2e18b6de 100644 --- a/packages/plugins/RedPacket/src/SiteAdaptor/SolanaRedpacketDialog/Routes.tsx +++ b/packages/plugins/RedPacket/src/SiteAdaptor/SolanaRedpacketDialog/Routes.tsx @@ -3,13 +3,19 @@ import { RoutePaths } from '../../constants.js' import { CreateSolRedPacket } from './CreateRedpacket.js' import { SolanaRedPacketConfirm } from './RedpacketConfirm.js' import { CustomCover } from '../views/CustomCover.js' +import { History } from '../views/History.js' +import { HistoryDetail } from '../views/HistoryDetail.js' + export function SolRedPacketRoutes() { return ( } /> - + + } /> + } /> + } /> } /> diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/components/ClaimRecord.tsx b/packages/plugins/RedPacket/src/SiteAdaptor/components/ClaimRecord.tsx index 24deb2e3bf92..81e8aeea08ab 100644 --- a/packages/plugins/RedPacket/src/SiteAdaptor/components/ClaimRecord.tsx +++ b/packages/plugins/RedPacket/src/SiteAdaptor/components/ClaimRecord.tsx @@ -1,13 +1,10 @@ import { Trans } from '@lingui/react/macro' import { Icons } from '@masknet/icons' import { EmojiAvatar } from '@masknet/shared' -import { NetworkPluginID } from '@masknet/shared-base' import { makeStyles } from '@masknet/theme' -import { useAccount } from '@masknet/web3-hooks-base' -import { EVMExplorerResolver } from '@masknet/web3-providers' +import { useAccount, useWeb3Utils } from '@masknet/web3-hooks-base' import type { FireflyRedPacketAPI } from '@masknet/web3-providers/types' import { formatBalance, isSameAddress } from '@masknet/web3-shared-base' -import { formatEthereumAddress } from '@masknet/web3-shared-evm' import { Typography } from '@mui/material' import { memo, type HTMLProps } from 'react' @@ -68,7 +65,9 @@ interface Props extends HTMLProps { export const ClaimRecord = memo(function ClaimRecord({ className, record, chainId, ...rest }: Props) { const { classes, theme } = useStyles() - const account = useAccount(NetworkPluginID.PLUGIN_EVM) + const account = useAccount() + const Utils = useWeb3Utils() + return (
@@ -84,8 +83,8 @@ export const ClaimRecord = memo(function ClaimRecord({ className, record, chainI
: null} - {formatEthereumAddress(record.creator, 4)} - + {Utils.formatAddress(record.creator, 4)} + diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/components/RedPacketActionButton.tsx b/packages/plugins/RedPacket/src/SiteAdaptor/components/RedPacketActionButton.tsx index 05a8e28c72fe..22e4e8c24109 100644 --- a/packages/plugins/RedPacket/src/SiteAdaptor/components/RedPacketActionButton.tsx +++ b/packages/plugins/RedPacket/src/SiteAdaptor/components/RedPacketActionButton.tsx @@ -1,15 +1,15 @@ import { Trans } from '@lingui/react/macro' -import { RedPacketMetaKey } from '@masknet/shared-base' +import { NetworkPluginID, RedPacketMetaKey } from '@masknet/shared-base' import { ActionButton, makeStyles, type ActionButtonProps } from '@masknet/theme' -import { FireflyRedPacket } from '@masknet/web3-providers' import { FireflyRedPacketAPI } from '@masknet/web3-providers/types' import type { ChainId } from '@masknet/web3-shared-evm' import { useMediaQuery, type Theme } from '@mui/material' import { memo, useCallback, useContext } from 'react' import { useAsyncFn } from 'react-use' import { CompositionTypeContext } from '../contexts/CompositionTypeContext.js' -import { useRefundCallback } from '../hooks/useRefundCallback.js' +import { useRefundCallback, useSolanaRefundCallback } from '../hooks/useRefundCallback.js' import { openComposition } from '../openComposition.js' +import { useEnvironmentContext } from '@masknet/web3-hooks-base' const useStyles = makeStyles()((theme) => { const smallQuery = `@media (max-width: ${theme.breakpoints.values.sm}px)` @@ -80,10 +80,13 @@ export const RedPacketActionButton = memo(function RedPacketActionButton({ ...rest }: Props) { const { classes, cx } = useStyles() + const { pluginID } = useEnvironmentContext() const isSmall = useMediaQuery((theme: Theme) => theme.breakpoints.down('sm')) const compositionType = useContext(CompositionTypeContext) const [{ loading: isRefunding }, refunded, refundCallback] = useRefundCallback(4, account, rpid, chainId) + const [{ loading: isSolanaRefunding }, solanaRefunded, refundSolanaCallback] = useSolanaRefundCallback(rpid) + const statusToTransMap = { [FireflyRedPacketAPI.RedPacketStatus.Send]: Send, [FireflyRedPacketAPI.RedPacketStatus.Expired]: Expired, @@ -96,14 +99,6 @@ export const RedPacketActionButton = memo(function RedPacketActionButton({ const [{ loading: isSharing }, shareCallback] = useAsyncFn(async () => { if (!shareFrom || !themeId || !createdAt) return - const payloadImage = await FireflyRedPacket.getPayloadUrlByThemeId( - themeId, - shareFrom, - tokenInfo.amount, - 'fungible', - tokenInfo.symbol, - Number(tokenInfo.decimals), - ) openComposition( RedPacketMetaKey, { @@ -125,23 +120,24 @@ export const RedPacketActionButton = memo(function RedPacketActionButton({ total: tokenInfo.amount, }, compositionType, - { claimRequirements: claim_strategy, payloadImage }, + { claimRequirements: claim_strategy }, ) }, []) - const redpacketStatus = refunded ? RedPacketStatus.Refund : propRedpacketStatus + const redpacketStatus = refunded || solanaRefunded ? RedPacketStatus.Refund : propRedpacketStatus const handleClick = useCallback(async () => { if (canResend) onResend?.() else if (redpacketStatus === RedPacketStatus.Send || redpacketStatus === RedPacketStatus.View) await shareCallback() - else if (redpacketStatus === RedPacketStatus.Refunding) await refundCallback() - }, [redpacketStatus, shareCallback, refundCallback, canResend, onResend]) + else if (redpacketStatus === RedPacketStatus.Refunding) + pluginID === NetworkPluginID.PLUGIN_SOLANA ? await refundSolanaCallback() : await refundCallback() + }, [redpacketStatus, shareCallback, refundCallback, canResend, onResend, refundSolanaCallback, pluginID]) return ( x.chainId === ChainId.Mainnet)!.backgroundGradient! const useStyles = makeStyles<{ background?: string; backgroundIcon?: string }>()(( @@ -197,28 +201,30 @@ export const RedPacketRecord = memo(function RedPacketRecord({ const [seen, redpacketRef] = useEverSeen() const chainId = history.chain_id - const { account } = useChainContext() - const networkDescriptor = useNetworkDescriptor(NetworkPluginID.PLUGIN_EVM, chainId) + const { account } = useChainContext() + const { pluginID } = useEnvironmentContext() + const networkDescriptor = useNetworkDescriptor(pluginID, chainId) const { classes, cx } = useStyles({ background: networkDescriptor?.backgroundGradient, backgroundIcon: networkDescriptor ? `url("${networkDescriptor.icon}")` : undefined, }) - // Only concern about MATIC token which has been renamed to POL - const { data: tokenAddress } = useRedpacketToken( - chainId, - history.trans_hash ?? '', - seen && token_symbol === 'MATIC' && !!history.trans_hash, - ) - const { data: token } = useFungibleToken(NetworkPluginID.PLUGIN_EVM, tokenAddress, undefined, { chainId }) - const tokenSymbol = token?.symbol ?? token_symbol + const tokenSymbol = token_symbol const contractAddress = useRedPacketConstant(chainId, 'HAPPY_RED_PACKET_ADDRESS_V4') const { data: redpacketRecord } = useQuery({ queryKey: ['redpacket', 'by-tx-hash', history.trans_hash], queryFn: history.trans_hash ? () => RedPacketRPC.getRedPacketRecord(history.trans_hash!) : skipToken, }) - const { data: createSuccessResult } = useCreateRedPacketReceipt(history.trans_hash ?? '', chainId) + const { data: createSuccessResult } = useCreateRedPacketReceipt( + pluginID === NetworkPluginID.PLUGIN_SOLANA ? history.redpacket_id : (history.trans_hash ?? ''), + chainId, + seen, + ) + + // Only concern about MATIC token which has been renamed to POL + const { data: token } = useFungibleToken(pluginID, createSuccessResult?.token_address, undefined, { chainId }) + const isViewStatus = redpacket_status === FireflyRedPacketAPI.RedPacketStatus.View const canResend = isViewStatus && !!redpacketRecord && !!createSuccessResult @@ -235,15 +241,16 @@ export const RedPacketRecord = memo(function RedPacketRecord({ size={36} badgeSize={16} chainId={chainId} - address={token?.address ?? tokenAddress!} + address={token?.address ?? createSuccessResult?.token_address} logoURL={token_logo} - symbol={token?.symbol} - name={token?.name} + symbol={token_symbol} + name={token_symbol} + disableBadge={pluginID === NetworkPluginID.PLUGIN_SOLANA} />
{formatBalance(amount, token_decimal, { significant: 2, isPrecise: true })}{' '} - {tokenSymbol ?? token?.symbol ?? '--'} + {tokenSymbol ?? token_symbol ?? '--'} {!onlyView ? @@ -286,7 +293,11 @@ export const RedPacketRecord = memo(function RedPacketRecord({ { - openWindow(EVMExplorerResolver.transactionLink(chainId, history.trans_hash!), '_blank') + const link = + pluginID === NetworkPluginID.PLUGIN_SOLANA ? + SolanaExplorerResolver.transactionLink(chainId, history.trans_hash) + : EVMExplorerResolver.transactionLink(chainId, history.trans_hash!) + openWindow(link, '_blank') }}> {t`View`} diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/components/RouterDialog.tsx b/packages/plugins/RedPacket/src/SiteAdaptor/components/RouterDialog.tsx index 8ea9902bb0c1..fd0bb6ecf8bd 100644 --- a/packages/plugins/RedPacket/src/SiteAdaptor/components/RouterDialog.tsx +++ b/packages/plugins/RedPacket/src/SiteAdaptor/components/RouterDialog.tsx @@ -88,6 +88,11 @@ export function RouterDialog(props: InjectedDialogProps & { pageMap: Record navigate({ pathname: RoutePaths.History, search: `tab=${HistoryTabs.Sent}` })} /> ), + [RoutePaths.CreateSolanaRedPacket]: ( + navigate({ pathname: RoutePaths.History, search: `tab=${HistoryTabs.Sent}` })} + /> + ), [RoutePaths.CreateNftRedPacket]: ( { diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/helpers/getRedpacket.ts b/packages/plugins/RedPacket/src/SiteAdaptor/helpers/getRedpacket.ts new file mode 100644 index 000000000000..40b09a569adf --- /dev/null +++ b/packages/plugins/RedPacket/src/SiteAdaptor/helpers/getRedpacket.ts @@ -0,0 +1,6 @@ +import { getRpProgram } from './getRpProgram.js' + +export async function getRedpacket(id: string) { + const program = await getRpProgram() + return program.account.redPacket.fetch(id) +} diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/helpers/refundNativeToken.ts b/packages/plugins/RedPacket/src/SiteAdaptor/helpers/refundNativeToken.ts new file mode 100644 index 000000000000..9de34d9ab199 --- /dev/null +++ b/packages/plugins/RedPacket/src/SiteAdaptor/helpers/refundNativeToken.ts @@ -0,0 +1,18 @@ +import { web3 } from '@coral-xyz/anchor' +import { getRpProgram } from './getRpProgram.js' +import * as SolanaWeb3 from /* webpackDefer: true */ '@solana/web3.js' + +export async function refundNativeToken(id: string, creator: SolanaWeb3.PublicKey) { + const program = await getRpProgram() + return program.methods + .withdrawWithNativeToken() + .accounts({ + // @ts-expect-error missing type + redPacket: new SolanaWeb3.PublicKey(id), + signer: new SolanaWeb3.PublicKey(creator), + systemProgram: web3.SystemProgram.programId, + }) + .rpc({ + commitment: 'confirmed', + }) +} diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/helpers/refundSplToken.ts b/packages/plugins/RedPacket/src/SiteAdaptor/helpers/refundSplToken.ts new file mode 100644 index 000000000000..7b632338e76a --- /dev/null +++ b/packages/plugins/RedPacket/src/SiteAdaptor/helpers/refundSplToken.ts @@ -0,0 +1,43 @@ +import { ASSOCIATED_TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync } from '@solana/spl-token' +import * as SolanaWeb3 from /* webpackDefer: true */ '@solana/web3.js' +import { getRpProgram } from './getRpProgram.js' + +export async function refundSplToken({ + id, + tokenMint, + tokenProgram, + tokenAccount, + creator, +}: { + id: string + tokenMint: SolanaWeb3.PublicKey + tokenProgram: SolanaWeb3.PublicKey | null + tokenAccount: SolanaWeb3.PublicKey | null + creator: SolanaWeb3.PublicKey +}) { + if (!tokenProgram || !tokenAccount) throw new Error('Token program or account not found') + + const program = await getRpProgram() + const vault = getAssociatedTokenAddressSync( + new SolanaWeb3.PublicKey(tokenMint), + new SolanaWeb3.PublicKey(id), + true, + new SolanaWeb3.PublicKey(tokenProgram), + ) + + return program.methods + .withdrawWithSplToken() + .accounts({ + // @ts-expect-error missing type + redPacket: new SolanaWeb3.PublicKey(id), + signer: creator, + vault, + tokenAccount, + tokenMint, + tokenProgram, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + }) + .rpc({ + commitment: 'confirmed', + }) +} diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateRedPacketReceipt.ts b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateRedPacketReceipt.ts index fc561c388fa8..90fa47620c93 100644 --- a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateRedPacketReceipt.ts +++ b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateRedPacketReceipt.ts @@ -1,10 +1,11 @@ import { NetworkPluginID } from '@masknet/shared-base' import REDPACKET_ABI from '@masknet/web3-contracts/abis/HappyRedPacketV4.json' with { type: 'json' } -import { useWeb3Connection } from '@masknet/web3-hooks-base' +import { useEnvironmentContext, useWeb3Connection } from '@masknet/web3-hooks-base' import { isSameAddress } from '@masknet/web3-shared-base' import { type ChainId, decodeEvents, useRedPacketConstants } from '@masknet/web3-shared-evm' import { useQuery } from '@tanstack/react-query' import type { AbiItem } from 'web3-utils' +import { getRedpacket } from '../helpers/getRedpacket.js' type CreationSuccessEventParams = { id: string @@ -24,29 +25,48 @@ type CreationSuccessEventParams = { /** account in string*/ total: string } -export function useCreateRedPacketReceipt(txHash: string, chainId: ChainId) { +export function useCreateRedPacketReceipt(txHashOrAccountId: string, chainId: ChainId, enabled?: boolean) { + const { pluginID } = useEnvironmentContext() const { HAPPY_RED_PACKET_ADDRESS_V4 } = useRedPacketConstants(chainId) - const Web3 = useWeb3Connection(NetworkPluginID.PLUGIN_EVM) + const Web3 = useWeb3Connection(pluginID) return useQuery({ + enabled, // eslint-disable-next-line @tanstack/query/exhaustive-deps - queryKey: ['redpacket', 'creation-success-params', chainId, txHash], + queryKey: ['redpacket', 'creation-success-params', chainId, txHashOrAccountId], queryFn: async () => { - if (!txHash || !Web3) return null + if (!txHashOrAccountId || !Web3) return null - const receipt = await Web3.getTransactionReceipt(txHash, { chainId }) - if (!receipt) return null + if (pluginID === NetworkPluginID.PLUGIN_EVM) { + const receipt = await Web3.getTransactionReceipt(txHashOrAccountId, { chainId }) + if (!receipt) return null - const log = receipt.logs.find((log) => isSameAddress(log.address, HAPPY_RED_PACKET_ADDRESS_V4)) - if (!log) return null + const log = receipt.logs.find((log) => isSameAddress(log.address, HAPPY_RED_PACKET_ADDRESS_V4)) + if (!log) return null - const eventParams = decodeEvents(REDPACKET_ABI as AbiItem[], [log]) as unknown as { - CreationSuccess: { - returnValues: CreationSuccessEventParams + const eventParams = decodeEvents(REDPACKET_ABI as AbiItem[], [log]) as unknown as { + CreationSuccess: { + returnValues: CreationSuccessEventParams + } } + + return eventParams.CreationSuccess.returnValues } + const result = await getRedpacket(txHashOrAccountId) - return eventParams.CreationSuccess.returnValues + return { + // id: result. + id: txHashOrAccountId, + creation_time: result.createTime.toString(), + creator: result.creator.toBase58(), + duration: '86400', + ifrandom: result.ifSpiltRandom, + message: result.message, + name: result.name, + number: result.totalNumber.toString(), + token_address: result.tokenAddress.toBase58(), + total: result.totalAmount.toString(), + } }, }) } diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useRefundCallback.ts b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useRefundCallback.ts deleted file mode 100644 index f3ca3eaad2a2..000000000000 --- a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useRefundCallback.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { useState } from 'react' -import { useAsyncFn } from 'react-use' -import { useChainContext } from '@masknet/web3-hooks-base' -import type { NetworkPluginID } from '@masknet/shared-base' -import { type ChainId, ContractTransaction } from '@masknet/web3-shared-evm' -import { EVMWeb3 } from '@masknet/web3-providers' -import { useRedPacketContract } from './useRedPacketContract.js' - -export function useRefundCallback(version: number, from: string, id?: string, expectedChainId?: ChainId) { - const { chainId } = useChainContext({ chainId: expectedChainId }) - const [isRefunded, setIsRefunded] = useState(false) - const redPacketContract = useRedPacketContract(chainId, version) - - const [state, refundCallback] = useAsyncFn(async () => { - if (!redPacketContract || !id) return - - setIsRefunded(false) - - const tx = await new ContractTransaction(redPacketContract).fillAll(redPacketContract.methods.refund(id), { - from, - }) - const hash = await EVMWeb3.sendTransaction(tx, { - chainId, - }) - setIsRefunded(true) - return hash - }, [id, redPacketContract, chainId, from]) - - return [state, isRefunded, refundCallback] as const -} diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useRefundCallback.tsx b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useRefundCallback.tsx new file mode 100644 index 000000000000..86c07a40aa96 --- /dev/null +++ b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useRefundCallback.tsx @@ -0,0 +1,76 @@ +import { useState } from 'react' +import { useAsyncFn } from 'react-use' +import { useChainContext } from '@masknet/web3-hooks-base' +import type { NetworkPluginID } from '@masknet/shared-base' +import { type ChainId, ContractTransaction } from '@masknet/web3-shared-evm' +import { EVMWeb3 } from '@masknet/web3-providers' +import { useRedPacketContract } from './useRedPacketContract.js' +import { getRedpacket } from '../helpers/getRedpacket.js' +import { refundNativeToken } from '../helpers/refundNativeToken.js' +import { getTokenAccount, getTokenProgram } from '../helpers/getTokenAccount.js' +import { refundSplToken } from '../helpers/refundSplToken.js' +import { queryClient } from '@masknet/shared-base-ui' +import { useCustomSnackbar } from '@masknet/theme' +import { Trans } from '@lingui/react/macro' + +export function useRefundCallback(version: number, from: string, id?: string, expectedChainId?: ChainId) { + const { chainId } = useChainContext({ chainId: expectedChainId }) + const [isRefunded, setIsRefunded] = useState(false) + const redPacketContract = useRedPacketContract(chainId, version) + + const [state, refundCallback] = useAsyncFn(async () => { + if (!redPacketContract || !id) return + + setIsRefunded(false) + + const tx = await new ContractTransaction(redPacketContract).fillAll(redPacketContract.methods.refund(id), { + from, + }) + const hash = await EVMWeb3.sendTransaction(tx, { + chainId, + }) + setIsRefunded(true) + return hash + }, [id, redPacketContract, chainId, from]) + + return [state, isRefunded, refundCallback] as const +} + +export function useSolanaRefundCallback(rpid: string) { + const { showSnackbar } = useCustomSnackbar() + const [isRefunded, setIsRefunded] = useState(false) + const [state, refundCallback] = useAsyncFn(async () => { + try { + if (!rpid) throw new Error('Failed to resolve redpacket id') + setIsRefunded(false) + const redpacket = await getRedpacket(rpid) + if (redpacket.tokenType === 0) { + await refundNativeToken(rpid, redpacket.creator) + } else { + const tokenMint = redpacket.tokenAddress + const tokenProgram = await getTokenProgram(tokenMint) + const tokenAccount = await getTokenAccount(tokenMint) + await refundSplToken({ + id: rpid, + tokenMint, + tokenProgram, + tokenAccount, + creator: redpacket.creator, + }) + } + + queryClient.invalidateQueries({ + queryKey: ['redpacket', 'history'], + }) + setIsRefunded(true) + showSnackbar(Refund Successfully, { variant: 'success' }) + } catch (error) { + if (error instanceof Error) { + showSnackbar(error.message, { variant: 'error' }) + } + throw error + } + }, [rpid]) + + return [state, isRefunded, refundCallback] as const +} diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/views/History.tsx b/packages/plugins/RedPacket/src/SiteAdaptor/views/History.tsx index fdca849d3f4a..3248abe2bc9f 100644 --- a/packages/plugins/RedPacket/src/SiteAdaptor/views/History.tsx +++ b/packages/plugins/RedPacket/src/SiteAdaptor/views/History.tsx @@ -1,9 +1,9 @@ import { Trans } from '@lingui/react/macro' import { RoutePaths } from '@masknet/plugin-redpacket' import { ElementAnchor, EmptyStatus, LoadingStatus, RestorableScroll, useParamTab } from '@masknet/shared' -import { EMPTY_LIST, type NetworkPluginID } from '@masknet/shared-base' +import { EMPTY_LIST, NetworkPluginID } from '@masknet/shared-base' import { LoadingBase, makeStyles } from '@masknet/theme' -import { useChainContext } from '@masknet/web3-hooks-base' +import { useChainContext, useEnvironmentContext } from '@masknet/web3-hooks-base' import { FireflyRedPacketAPI } from '@masknet/web3-providers/types' import { Typography } from '@mui/material' import { useCallback } from 'react' @@ -13,6 +13,8 @@ import { RedPacketRecord } from '../components/RedPacketRecord.js' import { useRedPacket } from '../contexts/RedPacketContext.js' import { useHandleCreateOrSelect } from '../hooks/useHandleCreateOrSelect.js' import { useRedPacketHistory } from '../hooks/useRedPacketHistory.js' +import { useSolRedpacket } from '../contexts/SolRedpacketContext.js' +import { useHandleSolanaCreateOrSelect } from '../hooks/useHandleSolanaCreateOrSelect.js' const useStyles = makeStyles()((theme) => ({ container: { @@ -38,8 +40,9 @@ const ActionType = FireflyRedPacketAPI.ActionType export function History() { const { classes } = useStyles() const [currentHistoryTab] = useParamTab(HistoryTabs.Claimed) - const { account } = useChainContext() + const { account } = useChainContext() const actionType = currentHistoryTab === HistoryTabs.Sent ? ActionType.Send : ActionType.Claim + const { pluginID } = useEnvironmentContext() const { data: histories = EMPTY_LIST, @@ -49,7 +52,11 @@ export function History() { hasNextPage, } = useRedPacketHistory(account, actionType) - const { creator } = useRedPacket() + const { creator: evmCreator } = useRedPacket() + const { creator: solCreator } = useSolRedpacket() + + const creator = pluginID === NetworkPluginID.PLUGIN_SOLANA ? solCreator : evmCreator + const navigate = useNavigate() const onClose = useCallback(() => { navigate(RoutePaths.Exit) @@ -60,6 +67,11 @@ export function History() { onClose, }) + const solanaSelectRedpacket = useHandleSolanaCreateOrSelect({ + senderName: creator, + onClose, + }) + if (isLoading) { return } @@ -86,7 +98,7 @@ export function History() { key={history.redpacket_id} history={history as FireflyRedPacketAPI.RedPacketSentInfo} onlyView={currentHistoryTab === HistoryTabs.Claimed} - onSelect={selectRedPacket} + onSelect={pluginID === NetworkPluginID.PLUGIN_SOLANA ? solanaSelectRedpacket : selectRedPacket} /> ))} {hasNextPage ? diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/views/HistoryDetail.tsx b/packages/plugins/RedPacket/src/SiteAdaptor/views/HistoryDetail.tsx index 295c57a1d614..90e49614a216 100644 --- a/packages/plugins/RedPacket/src/SiteAdaptor/views/HistoryDetail.tsx +++ b/packages/plugins/RedPacket/src/SiteAdaptor/views/HistoryDetail.tsx @@ -1,4 +1,4 @@ -import { createIndicator, EMPTY_LIST } from '@masknet/shared-base' +import { createIndicator, EMPTY_LIST, NetworkPluginID } from '@masknet/shared-base' import { makeStyles } from '@masknet/theme' import { FireflyRedPacket } from '@masknet/web3-providers' import type { FireflyRedPacketAPI } from '@masknet/web3-providers/types' @@ -10,6 +10,9 @@ import { ClaimRecord } from '../components/ClaimRecord.js' import { RedPacketRecord } from '../components/RedPacketRecord.js' import { EmptyStatus, LoadingStatus } from '@masknet/shared' import { Trans } from '@lingui/react/macro' +import { useEnvironmentContext } from '@masknet/web3-hooks-base' +import { getRpProgram } from '../helpers/getRpProgram.js' +import * as SolanaWeb3 from /* webpackDefer: true */ '@solana/web3.js' const useStyles = makeStyles()((theme) => ({ container: { @@ -36,6 +39,7 @@ const useStyles = makeStyles()((theme) => ({ export function HistoryDetail() { const { classes, cx } = useStyles() + const { pluginID } = useEnvironmentContext() const location = useLocation() const history = location.state.history as FireflyRedPacketAPI.RedPacketSentInfo const [params] = useSearchParams() @@ -46,17 +50,45 @@ export function HistoryDetail() { const { data, isLoading } = useInfiniteQuery({ enabled: !!rpid, - queryKey: ['redpacket', 'claim-history', rpid, chainId], + queryKey: ['redpacket', 'claim-history', pluginID, rpid, chainId], initialPageParam: undefined as string | undefined, queryFn: rpid ? async ({ pageParam }) => { - const res = await FireflyRedPacket.getClaimHistory( - rpid, - chainId, - createIndicator(undefined, pageParam), - ) - return res + if (pluginID === NetworkPluginID.PLUGIN_SOLANA) { + const program = await getRpProgram() + const records = await program.account.claimRecord.all([ + { + memcmp: { + offset: 8, // Adjust the offset based on your account structure + bytes: new SolanaWeb3.PublicKey(rpid).toBase58(), + }, + }, + ]) + + const results = records.map(({ account }) => ({ + creator: account.claimer.toBase58(), + claim_platform: [], + token_amounts: account.amount.toString(), + token_symbol: history.token_symbol, + token_decimal: history.token_decimal, + ens_name: '', + })) + + return { + chain_id: chainId, + cursor: '', + list: results, + } + } else { + const res = await FireflyRedPacket.getClaimHistory( + rpid, + chainId, + createIndicator(undefined, pageParam), + ) + + return res + } } : skipToken, getNextPageParam: (lastPage) => lastPage.cursor, diff --git a/packages/plugins/RedPacket/src/constants.ts b/packages/plugins/RedPacket/src/constants.ts index c3a07fda00e1..e3d51d62e87f 100644 --- a/packages/plugins/RedPacket/src/constants.ts +++ b/packages/plugins/RedPacket/src/constants.ts @@ -12,7 +12,7 @@ export const RED_PACKET_MIN_SHARES = 1 export const RED_PACKET_MAX_SHARES = 255 export const SOL_REDPACKET_MAX_SHARES = 200 -export const SOL_REDPACKET_CREATE_DEFAULT_GAS = '10000000' +export const SOL_REDPACKET_CREATE_DEFAULT_GAS = '1000000' export const DEFAULT_DURATION = 1000 * 60 * 60 * 24 // 24 hours export const enum RoutePaths { Create = '/create', diff --git a/packages/plugins/RedPacket/src/locale/en-US.json b/packages/plugins/RedPacket/src/locale/en-US.json index f5bef675e410..baeb8fa35f5e 100644 --- a/packages/plugins/RedPacket/src/locale/en-US.json +++ b/packages/plugins/RedPacket/src/locale/en-US.json @@ -202,6 +202,7 @@ "aLc9As": ["Received time:"], "DkHkqH": ["Recommended dimensions: 1016 × 672 px"], "J0LAHU": ["Refund"], + "4xCNxC": ["Refund Successfully"], "/BI0y9": ["Refunded"], "GShMK6": ["Refunding"], "mkude1": ["Repost"], diff --git a/packages/plugins/RedPacket/src/locale/en-US.po b/packages/plugins/RedPacket/src/locale/en-US.po index c67c515efbf6..f8eef611e03a 100644 --- a/packages/plugins/RedPacket/src/locale/en-US.po +++ b/packages/plugins/RedPacket/src/locale/en-US.po @@ -651,6 +651,10 @@ msgstr "" msgid "Refund" msgstr "" +#: src/SiteAdaptor/hooks/useRefundCallback.tsx +msgid "Refund Successfully" +msgstr "" + #: src/SiteAdaptor/components/RedPacketEnvelope.tsx msgid "Refunded" msgstr "" diff --git a/packages/plugins/RedPacket/src/locale/ja-JP.json b/packages/plugins/RedPacket/src/locale/ja-JP.json index f3d1aeb142cf..73d5dc262692 100644 --- a/packages/plugins/RedPacket/src/locale/ja-JP.json +++ b/packages/plugins/RedPacket/src/locale/ja-JP.json @@ -201,6 +201,7 @@ "aLc9As": ["Received time:"], "DkHkqH": ["Recommended dimensions: 1016 × 672 px"], "J0LAHU": ["返金"], + "4xCNxC": ["Refund Successfully"], "/BI0y9": ["Refunded"], "GShMK6": ["返金処理中"], "mkude1": ["リポスト"], diff --git a/packages/plugins/RedPacket/src/locale/ja-JP.po b/packages/plugins/RedPacket/src/locale/ja-JP.po index df41d7a781ff..5abcb4fa34b7 100644 --- a/packages/plugins/RedPacket/src/locale/ja-JP.po +++ b/packages/plugins/RedPacket/src/locale/ja-JP.po @@ -656,6 +656,10 @@ msgstr "" msgid "Refund" msgstr "返金" +#: src/SiteAdaptor/hooks/useRefundCallback.tsx +msgid "Refund Successfully" +msgstr "" + #: src/SiteAdaptor/components/RedPacketEnvelope.tsx msgid "Refunded" msgstr "" diff --git a/packages/plugins/RedPacket/src/locale/ko-KR.json b/packages/plugins/RedPacket/src/locale/ko-KR.json index 2aff79815311..311fc3da6a7d 100644 --- a/packages/plugins/RedPacket/src/locale/ko-KR.json +++ b/packages/plugins/RedPacket/src/locale/ko-KR.json @@ -202,6 +202,7 @@ "aLc9As": ["Received time:"], "DkHkqH": ["Recommended dimensions: 1016 × 672 px"], "J0LAHU": ["리펀"], + "4xCNxC": ["Refund Successfully"], "/BI0y9": ["Refunded"], "GShMK6": ["환급 중"], "mkude1": ["Repost"], diff --git a/packages/plugins/RedPacket/src/locale/ko-KR.po b/packages/plugins/RedPacket/src/locale/ko-KR.po index 188af1b3f253..4228aae90af9 100644 --- a/packages/plugins/RedPacket/src/locale/ko-KR.po +++ b/packages/plugins/RedPacket/src/locale/ko-KR.po @@ -656,6 +656,10 @@ msgstr "" msgid "Refund" msgstr "리펀" +#: src/SiteAdaptor/hooks/useRefundCallback.tsx +msgid "Refund Successfully" +msgstr "" + #: src/SiteAdaptor/components/RedPacketEnvelope.tsx msgid "Refunded" msgstr "" diff --git a/packages/plugins/RedPacket/src/locale/zh-CN.json b/packages/plugins/RedPacket/src/locale/zh-CN.json index fbd259be6288..3acc23f88f0c 100644 --- a/packages/plugins/RedPacket/src/locale/zh-CN.json +++ b/packages/plugins/RedPacket/src/locale/zh-CN.json @@ -202,6 +202,7 @@ "aLc9As": ["Received time:"], "DkHkqH": ["Recommended dimensions: 1016 × 672 px"], "J0LAHU": ["退款"], + "4xCNxC": ["Refund Successfully"], "/BI0y9": ["Refunded"], "GShMK6": ["退款中"], "mkude1": ["Repost"], diff --git a/packages/plugins/RedPacket/src/locale/zh-CN.po b/packages/plugins/RedPacket/src/locale/zh-CN.po index 9f02aa6c5594..a05d20323efe 100644 --- a/packages/plugins/RedPacket/src/locale/zh-CN.po +++ b/packages/plugins/RedPacket/src/locale/zh-CN.po @@ -656,6 +656,10 @@ msgstr "" msgid "Refund" msgstr "退款" +#: src/SiteAdaptor/hooks/useRefundCallback.tsx +msgid "Refund Successfully" +msgstr "" + #: src/SiteAdaptor/components/RedPacketEnvelope.tsx msgid "Refunded" msgstr "" diff --git a/packages/plugins/RedPacket/src/locale/zh-TW.json b/packages/plugins/RedPacket/src/locale/zh-TW.json index 94493ee65a74..120af0b32757 100644 --- a/packages/plugins/RedPacket/src/locale/zh-TW.json +++ b/packages/plugins/RedPacket/src/locale/zh-TW.json @@ -202,6 +202,7 @@ "aLc9As": ["Received time:"], "DkHkqH": ["Recommended dimensions: 1016 × 672 px"], "J0LAHU": ["退款"], + "4xCNxC": ["Refund Successfully"], "/BI0y9": ["Refunded"], "GShMK6": ["退款中"], "mkude1": ["Repost"], diff --git a/packages/plugins/RedPacket/src/locale/zh-TW.po b/packages/plugins/RedPacket/src/locale/zh-TW.po index c56b4bb104f3..5b180ffb049b 100644 --- a/packages/plugins/RedPacket/src/locale/zh-TW.po +++ b/packages/plugins/RedPacket/src/locale/zh-TW.po @@ -656,6 +656,10 @@ msgstr "" msgid "Refund" msgstr "退款" +#: src/SiteAdaptor/hooks/useRefundCallback.tsx +msgid "Refund Successfully" +msgstr "" + #: src/SiteAdaptor/components/RedPacketEnvelope.tsx msgid "Refunded" msgstr "" diff --git a/packages/web3-shared/solana/src/constants/descriptors.ts b/packages/web3-shared/solana/src/constants/descriptors.ts index 31feb84b5d2f..a9a3176ea20f 100644 --- a/packages/web3-shared/solana/src/constants/descriptors.ts +++ b/packages/web3-shared/solana/src/constants/descriptors.ts @@ -112,6 +112,8 @@ export const NETWORK_DESCRIPTORS: ReadonlyArray> = [ {