Skip to content

Commit

Permalink
fix(Solana): mf-3170 fails to load token list (#8458)
Browse files Browse the repository at this point in the history
  • Loading branch information
UncleBill committed Jan 12, 2023
1 parent 98b3e4f commit c50615f
Show file tree
Hide file tree
Showing 21 changed files with 87 additions and 72 deletions.
Expand Up @@ -52,7 +52,7 @@ export const TransferERC20 = memo<TransferERC20Props>(({ token }) => {
const { value: defaultGasPrice = '0' } = useGasPrice(NetworkPluginID.PLUGIN_EVM)

const [selectedToken, setSelectedToken] = useState(token)
const selectFungibleToken = useSelectFungibleToken(NetworkPluginID.PLUGIN_EVM)
const selectFungibleToken = useSelectFungibleToken<void, NetworkPluginID.PLUGIN_EVM>()
const { chainId } = useChainContext<NetworkPluginID.PLUGIN_EVM>()
const is1559Supported = useMemo(() => chainResolver.isSupport(chainId, 'EIP1559'), [chainId])

Expand Down
Expand Up @@ -71,7 +71,7 @@ export function ExchangeTokenPanel(props: ExchangeTokenPanelProps) {
const { t } = useI18N()
const { classes } = useStyles()
// #region select token dialog
const selectFungibleToken = useSelectFungibleToken(NetworkPluginID.PLUGIN_EVM)
const selectFungibleToken = useSelectFungibleToken<void, NetworkPluginID.PLUGIN_EVM>()
const onSelectTokenChipClick = useCallback(async () => {
const picked = await selectFungibleToken({
disableNativeToken: isSell,
Expand Down
@@ -1,7 +1,7 @@
import { useCallback } from 'react'
import type { MaskFixedSizeListProps, MaskTextFieldProps } from '@masknet/theme'
import { useSelectFungibleToken, FungibleTokenInput, FungibleTokenInputProps } from '@masknet/shared'
import { NetworkPluginID } from '@masknet/shared-base'
import type { NetworkPluginID } from '@masknet/shared-base'
import { useI18N } from '../../../utils/index.js'
import type { Web3Helper } from '@masknet/web3-helpers'

Expand Down Expand Up @@ -44,7 +44,7 @@ export function SelectTokenAmountPanel(props: SelectTokenAmountPanelProps) {
} = props

// #region select token
const selectFungibleToken = useSelectFungibleToken(NetworkPluginID.PLUGIN_EVM)
const selectFungibleToken = useSelectFungibleToken<void, NetworkPluginID.PLUGIN_EVM>()
const onSelectTokenChipClick = useCallback(async () => {
const picked = await selectFungibleToken({
disableNativeToken,
Expand Down
2 changes: 1 addition & 1 deletion packages/mask/src/plugins/ITO/SNSAdaptor/SwapDialog.tsx
Expand Up @@ -111,7 +111,7 @@ export function SwapDialog(props: SwapDialogProps) {
swapAmount.isZero() ? '' : leftShift(swapAmount, swapToken?.decimals).toFixed(),
)
// #region select token
const selectFungibleToken = useSelectFungibleToken(NetworkPluginID.PLUGIN_EVM)
const selectFungibleToken = useSelectFungibleToken<void, NetworkPluginID.PLUGIN_EVM>()
const onSelectTokenChipClick = useCallback(async () => {
const picked = await selectFungibleToken({
disableNativeToken: !exchangeTokens.some((x) => isNativeTokenAddress(x.address)),
Expand Down
2 changes: 1 addition & 1 deletion packages/mask/src/plugins/ITO/SNSAdaptor/UnlockDialog.tsx
Expand Up @@ -42,7 +42,7 @@ export function UnlockDialog(props: UnlockDialogProps) {
const { ITO2_CONTRACT_ADDRESS } = useITOConstants(chainId)
// #region select token
const [token, setToken] = useState<FungibleToken<ChainId, SchemaType.ERC20>>(tokens[0])
const selectFungibleToken = useSelectFungibleToken(NetworkPluginID.PLUGIN_EVM)
const selectFungibleToken = useSelectFungibleToken<void, NetworkPluginID.PLUGIN_EVM>()
const onSelectTokenChipClick = useCallback(async () => {
const picked = await selectFungibleToken({
disableNativeToken: true,
Expand Down
Expand Up @@ -93,7 +93,7 @@ export function RedPacketERC20Form(props: RedPacketFormProps) {
origin?.token,
)

const selectFungibleToken = useSelectFungibleToken(NetworkPluginID.PLUGIN_EVM)
const selectFungibleToken = useSelectFungibleToken<void, NetworkPluginID.PLUGIN_EVM>()
const onSelectTokenChipClick = useCallback(async () => {
const picked = await selectFungibleToken({
disableNativeToken: false,
Expand Down
Expand Up @@ -28,15 +28,15 @@ export const TokenSection: FC<Props> = ({ className, ...rest }) => {
const { classes, cx } = useStyles()
const { token, setToken, amount, setAmount } = useTip()
const { pluginID } = useNetworkContext()
const { account, chainId } = useChainContext<NetworkPluginID.PLUGIN_EVM>()
const { account, chainId } = useChainContext()

// balance
const { value: tokenBalance = '0' } = useFungibleTokenBalance(NetworkPluginID.PLUGIN_EVM, token?.address, {
const { value: tokenBalance = '0' } = useFungibleTokenBalance(pluginID, token?.address, {
chainId,
account,
})
const { gasPrice } = useGasConfig(chainId)
const { value: defaultGasPrice = '1' } = useGasPrice(NetworkPluginID.PLUGIN_EVM, { chainId })
const { value: defaultGasPrice = '1' } = useGasPrice(pluginID, { chainId })
const isNativeToken = useMemo(() => isNativeTokenAddress(token?.address), [token?.address])

const maxAmount = useMemo(() => {
Expand All @@ -50,14 +50,15 @@ export const TokenSection: FC<Props> = ({ className, ...rest }) => {
const selectFungibleToken = useSelectFungibleToken()
const onSelectTokenChipClick = useCallback(async () => {
const picked = await selectFungibleToken({
pluginID,
chainId,
disableNativeToken: false,
selectedTokens: token ? [token.address] : [],
})
if (picked) {
setToken(picked)
}
}, [selectFungibleToken, token?.address, chainId])
}, [selectFungibleToken, token?.address, pluginID, chainId])
// #endregion

return (
Expand Down
22 changes: 10 additions & 12 deletions packages/mask/src/plugins/Tips/contexts/Tip/TipTaskProvider.tsx
Expand Up @@ -5,6 +5,7 @@ import {
useNonFungibleTokenContract,
useNetworkContext,
useChainContext,
getDefaultChainId,
} from '@masknet/web3-hooks-base'
import { isSameAddress, SocialAccount } from '@masknet/web3-shared-base'
import type { ChainId, GasConfig } from '@masknet/web3-shared-evm'
Expand Down Expand Up @@ -47,7 +48,7 @@ function useDirtyDetection(deps: any[]): [boolean, Dispatch<SetStateAction<boole

export const TipTaskProvider: FC<React.PropsWithChildren<Props>> = memo(({ children, task }) => {
const { pluginID, setPluginID } = useNetworkContext()
const { chainId } = useChainContext()
const { chainId, setChainId } = useChainContext()

const [_recipientAddress, setRecipient] = useState(task.recipient ?? '')
const recipients = useRecipients(pluginID, task.accounts)
Expand Down Expand Up @@ -98,12 +99,11 @@ export const TipTaskProvider: FC<React.PropsWithChildren<Props>> = memo(({ child

useEffect(reset, [chainId])

const wrappedSendTip = useCallback(() => {
setIsDirty(false)
return sendTip()
}, [sendTip])

const contextValue = useMemo((): TipContextOptions => {
const wrappedSendTip = () => {
setIsDirty(false)
return sendTip()
}
return {
recipient,
recipientSnsId: task.recipientSnsId || '',
Expand Down Expand Up @@ -146,7 +146,7 @@ export const TipTaskProvider: FC<React.PropsWithChildren<Props>> = memo(({ child
nonFungibleTokenContract,
nonFungibleTokenAddress,
token,
wrappedSendTip,
sendTip,
isSending,
reset,
gasOption,
Expand All @@ -157,11 +157,9 @@ export const TipTaskProvider: FC<React.PropsWithChildren<Props>> = memo(({ child
])

useEffect(() => {
if (recipient?.pluginID) {
setPluginID(recipient.pluginID)
} else {
setPluginID(NetworkPluginID.PLUGIN_EVM)
}
const pid = recipient?.pluginID ?? NetworkPluginID.PLUGIN_EVM
setPluginID(pid)
setChainId(getDefaultChainId(pid))
}, [recipient?.pluginID])

return <TipContext.Provider value={contextValue}>{children}</TipContext.Provider>
Expand Down
11 changes: 5 additions & 6 deletions packages/mask/src/plugins/Tips/contexts/TipTaskManager.tsx
@@ -1,8 +1,7 @@
import { FC, useCallback, useEffect, useState } from 'react'
import { isEqual } from 'lodash-es'
import { EMPTY_LIST, NetworkPluginID } from '@masknet/shared-base'
import { Web3ContextProvider } from '@masknet/web3-hooks-base'
import { ChainId } from '@masknet/web3-shared-evm'
import { EMPTY_LIST } from '@masknet/shared-base'
import { getDefaultChainId, Web3ContextProvider } from '@masknet/web3-hooks-base'
import { isSameAddress } from '@masknet/web3-shared-base'
import { TipDialog } from '../components/index.js'
import { PluginTipsMessages } from '../messages.js'
Expand Down Expand Up @@ -46,13 +45,13 @@ export const TipTaskManager: FC<React.PropsWithChildren<{}>> = ({ children }) =>
<>
{tasks.map((task) => {
const tipsAccount = task.accounts.find((x) => isSameAddress(x.address, task.recipient))

const pluginID = tipsAccount?.pluginID ?? task.accounts[0].pluginID
return (
<Web3ContextProvider
key={task.id}
value={{
pluginID: tipsAccount?.pluginID ?? NetworkPluginID.PLUGIN_EVM,
chainId: ChainId.Mainnet,
pluginID,
chainId: getDefaultChainId(pluginID),
}}>
<TipTaskProvider key={task.id} task={task}>
<TipsTransactionProvider>
Expand Down
12 changes: 8 additions & 4 deletions packages/plugins/Solana/src/state/Connection/connection.ts
Expand Up @@ -148,10 +148,9 @@ class Connection implements BaseConnection {
Number.parseInt(amount, 10),
),
)
const blockHash = await connection.getRecentBlockhash()
const blockHash = await connection.getLatestBlockhash()
transaction.feePayer = payerPubkey
transaction.recentBlockhash = blockHash.blockhash

const signature = await this.sendTransaction(transaction)
return signature
}
Expand Down Expand Up @@ -272,14 +271,19 @@ class Connection implements BaseConnection {
const options = this.getOptions(initial)
if (!options.account) return {}
const { data: assets } = await SolanaFungible.getAssets(options.account, options)
const records = assets.reduce(
(map: Record<string, string>, asset) => ({ ...map, [asset.address]: asset.balance }),
const records = assets.reduce<Record<string, string>>(
(map, asset) => ({ ...map, [asset.address]: asset.balance }),
{},
)
const nativeTokenAddress = getNativeTokenAddress(options.chainId)
if (listOfAddress.includes(nativeTokenAddress)) {
records[nativeTokenAddress] = await this.getNativeTokenBalance(options)
}
// In the token picker UI, if balance of a token is undefined, then it
// will keep loading. We set it 0 to walk around that, since fetching is done.
listOfAddress.forEach((address) => {
records[address] = records[address] ?? '0'
})
return records
}
getNonFungibleTokensBalance(
Expand Down
@@ -1,5 +1,5 @@
import { TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID } from '@solana/spl-token'
import { Connection, PublicKey, Commitment, Transaction } from '@solana/web3.js'
import { type Connection, PublicKey, Commitment, Transaction } from '@solana/web3.js'
import { createAssociatedTokenAccountInstruction } from './createAssociatedTokenAccountInstruction.js'
import { getAccountInfo } from './getAccountInfo.js'
import { getAssociatedTokenAddress } from './getAssociatedTokenAddress.js'
Expand Down Expand Up @@ -47,14 +47,18 @@ export async function getOrCreateAssociatedTokenAccount(
),
)

const blockHash = await connection.getRecentBlockhash()
const blockHash = await connection.getLatestBlockhash()
transaction.feePayer = payer
transaction.recentBlockhash = blockHash.blockhash
const signed = await signTransaction(transaction)

const signature = await connection.sendRawTransaction(signed.serialize())

await connection.confirmTransaction(signature)
await connection.confirmTransaction({
signature,
blockhash: blockHash.blockhash,
lastValidBlockHeight: blockHash.lastValidBlockHeight,
})
} catch (error: unknown) {
// Ignore all errors; for now there is no API-compatible way to selectively ignore the expected
// instruction error if the associated account exists already.
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/Solana/src/state/Hub/hub.ts
Expand Up @@ -13,7 +13,7 @@ import { ChainId, GasOption, SchemaType } from '@masknet/web3-shared-solana'
import type { SolanaHub } from './types.js'

class HubFungibleClient extends HubStateFungibleClient<ChainId, SchemaType> {
protected getFungibleProviders(initial?: HubOptions<ChainId>) {
protected override getProviders(initial?: HubOptions<ChainId>) {
const options = this.getOptions(initial)

// only the first page is available
Expand Down
21 changes: 9 additions & 12 deletions packages/shared/src/UI/components/FungibleTokenList/index.tsx
Expand Up @@ -16,7 +16,6 @@ import { Box, Stack, Typography } from '@mui/material'
import { useSharedI18N } from '../../../locales/index.js'
import {
useBlockedFungibleTokens,
useChainContext,
useNetworkContext,
useFungibleAssets,
useFungibleToken,
Expand All @@ -25,6 +24,7 @@ import {
useFungibleTokensFromTokenList,
useTrustedFungibleTokens,
useWeb3State,
useAccount,
} from '@masknet/web3-hooks-base'
import type { Web3Helper } from '@masknet/web3-helpers'
import {
Expand Down Expand Up @@ -137,11 +137,10 @@ export const FungibleTokenList = forwardRef(
// #endregion

const { pluginID } = useNetworkContext<T>(props.pluginID)
const { account, chainId } = useChainContext({
chainId: props.chainId,
})
const account = useAccount(pluginID)
const chainId = props.chainId
const { Token, Others } = useWeb3State<'all'>(pluginID)
const { value: fungibleTokens = EMPTY_LIST } = useFungibleTokensFromTokenList(pluginID, { chainId })
const { value: fungibleTokens = EMPTY_LIST, error } = useFungibleTokensFromTokenList(pluginID, { chainId })
const trustedFungibleTokens = useTrustedFungibleTokens(pluginID, undefined, chainId)
const blockedFungibleTokens = useBlockedFungibleTokens(pluginID)
const nativeToken = useMemo(() => Others?.chainResolver.nativeCurrency(chainId), [chainId])
Expand All @@ -166,13 +165,9 @@ export const FungibleTokenList = forwardRef(
{ account, chainId },
)

const { value: fungibleAssets = EMPTY_LIST, loading: loadingFungibleAssets } = useFungibleAssets(
pluginID,
undefined,
{
chainId,
},
)
const { value: fungibleAssets = EMPTY_LIST } = useFungibleAssets(pluginID, undefined, {
chainId,
})

// To avoid SearchableList re-render, reduce the dep
const sortedFungibleTokensForManage = useMemo(() => {
Expand Down Expand Up @@ -433,3 +428,5 @@ export const FungibleTokenList = forwardRef(
)
},
)

FungibleTokenList.displayName = 'FungibleTokenList'
9 changes: 4 additions & 5 deletions packages/shared/src/contexts/SharedContextProvider.tsx
@@ -1,12 +1,11 @@
import { compose } from '@masknet/shared-base'
import type { PropsWithChildren } from 'react'
import { BaseSharedUIProvider } from './base/index.js'
import { CommonUIProvider } from './common/index.js'

export function SharedContextProvider({ children }: PropsWithChildren<{}>) {
return compose(
(children) => BaseSharedUIProvider({ children }),
(children) => CommonUIProvider({ children }),
<>{children}</>,
return (
<BaseSharedUIProvider>
<CommonUIProvider>{children}</CommonUIProvider>
</BaseSharedUIProvider>
)
}
Expand Up @@ -12,9 +12,7 @@ const { TaskManagerContext, TaskManagerProvider: SelectFungibleTokenProvider } =
Web3Helper.FungibleTokenScope<'all'> | null
>(SelectFungibleTokenDialog)

export function useSelectFungibleToken<S extends 'all' | void = void, T extends NetworkPluginID = NetworkPluginID>(
pluginID?: T,
) {
export function useSelectFungibleToken<S extends 'all' | void = void, T extends NetworkPluginID = NetworkPluginID>() {
const context = useContext(TaskManagerContext) as ContextOptions<
SelectFungibleTokenDialogProps,
Web3Helper.FungibleTokenScope<S, T>
Expand Down
12 changes: 6 additions & 6 deletions packages/shared/src/contexts/common/index.tsx
Expand Up @@ -2,18 +2,18 @@ import type { PropsWithChildren } from 'react'
import { ConfirmProvider } from './Confirm/index.js'
import { SelectFungibleTokenProvider } from './SelectFungibleTokenDialog/index.js'
import { SelectGasSettingsProvider } from './SelectAdvancedSettingsDialog/index.js'
import { compose } from '@masknet/shared-base'

export * from './Confirm/index.js'
export * from './SelectFungibleTokenDialog/index.js'
export * from './SelectAdvancedSettingsDialog/index.js'
export * from './Log/index.js'

export function CommonUIProvider({ children }: PropsWithChildren<{}>) {
return compose(
(children) => ConfirmProvider({ children }),
(children) => SelectFungibleTokenProvider({ children }),
(children) => SelectGasSettingsProvider({ children }),
<>{children}</>,
return (
<ConfirmProvider>
<SelectFungibleTokenProvider>
<SelectGasSettingsProvider>{children}</SelectGasSettingsProvider>
</SelectFungibleTokenProvider>
</ConfirmProvider>
)
}
Expand Up @@ -23,7 +23,7 @@ const useStyles = makeStyles<StyleProps>()((theme, { compact, isDashboard }) =>
display: 'flex',
flexDirection: 'column',
overflow: 'auto',
'-ms-overflow-style': 'none',
msOverflowStyle: 'none',
scrollbarWidth: 'none',
'&::-webkit-scrollbar': {
display: 'none',
Expand Down
3 changes: 2 additions & 1 deletion packages/theme/src/Components/TextField/index.tsx
Expand Up @@ -73,7 +73,7 @@ export interface MaskTextFieldProps extends Exclude<StandardTextFieldProps, 'var
}

export const MaskTextField = forwardRef((props: MaskTextFieldProps, ref: ForwardedRef<any>) => {
const { label, sx, required = false, className, wrapperProps, ...rest } = props
const { label, sx, required = false, className, wrapperProps, helperText, ...rest } = props
const inputProps = (props.InputProps as InputProps) ?? {}
const { classes } = useStyles()
return (
Expand All @@ -96,6 +96,7 @@ export const MaskTextField = forwardRef((props: MaskTextFieldProps, ref: Forward
classes={{ root: classes.field }}
variant="standard"
required={required}
helperText={helperText}
InputProps={{
disableUnderline: true,
className: classes.input,
Expand Down
4 changes: 4 additions & 0 deletions packages/web3-hooks/base/src/useDefaultChainId.ts
Expand Up @@ -15,3 +15,7 @@ export function useDefaultChainId<T extends NetworkPluginID>(expectedPluginID?:
const { pluginID } = useNetworkContext<T>(expectedPluginID)
return DEFAULT_CHAIN_ID[pluginID]
}

export function getDefaultChainId<T extends NetworkPluginID>(pluginID: T) {
return DEFAULT_CHAIN_ID[pluginID]
}

0 comments on commit c50615f

Please sign in to comment.