fix: fixed the wallet auto connection (#341)
* fix: fixed the wallet auto connection * tidy: refactor * fix: re-enabled account change support * tidy: refactor
This commit is contained in:
parent
267b968c4a
commit
cec9e50955
@ -2,8 +2,8 @@ import { useShuttle } from '@delphi-labs/shuttle-react'
|
|||||||
import BigNumber from 'bignumber.js'
|
import BigNumber from 'bignumber.js'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
import useClipboard from 'react-use-clipboard'
|
|
||||||
import { useLocation, useNavigate } from 'react-router-dom'
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
|
import useClipboard from 'react-use-clipboard'
|
||||||
|
|
||||||
import Button from 'components/Button'
|
import Button from 'components/Button'
|
||||||
import { CircularProgress } from 'components/CircularProgress'
|
import { CircularProgress } from 'components/CircularProgress'
|
||||||
@ -60,7 +60,7 @@ export default function WalletConnectedButton() {
|
|||||||
const onDisconnectWallet = () => {
|
const onDisconnectWallet = () => {
|
||||||
if (!currentWallet) return
|
if (!currentWallet) return
|
||||||
disconnectWallet(currentWallet)
|
disconnectWallet(currentWallet)
|
||||||
useStore.setState({ client: undefined, balances: [] })
|
useStore.setState({ client: undefined, address: undefined, accounts: null, balances: [] })
|
||||||
navigate(getRoute(getPage(pathname)))
|
navigate(getRoute(getPage(pathname)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
94
src/components/Wallet/WalletConnecting.tsx
Normal file
94
src/components/Wallet/WalletConnecting.tsx
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
|
||||||
|
import { useShuttle } from '@delphi-labs/shuttle-react'
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
|
import { CircularProgress } from 'components/CircularProgress'
|
||||||
|
import FullOverlayContent from 'components/FullOverlayContent'
|
||||||
|
import WalletSelect from 'components/Wallet//WalletSelect'
|
||||||
|
import WalletFetchBalancesAndAccounts from 'components/Wallet/WalletFetchBalancesAndAccounts'
|
||||||
|
import { CHAINS } from 'constants/chains'
|
||||||
|
import { ENV } from 'constants/env'
|
||||||
|
import useToggle from 'hooks/useToggle'
|
||||||
|
import useStore from 'store'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
autoConnect?: boolean
|
||||||
|
providerId?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentChainId = ENV.CHAIN_ID
|
||||||
|
const currentChain = CHAINS[currentChainId]
|
||||||
|
|
||||||
|
const mapErrorMessages = (providerId: string, errorMessage: string) => {
|
||||||
|
if (providerId === 'station') {
|
||||||
|
if (errorMessage.match('Wallet not connected to the network with chainId')) {
|
||||||
|
return `Your wallet is not connected to the correct network. Please switch your wallet to the ${currentChain.name} network`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function WalletConnecting(props: Props) {
|
||||||
|
const { extensionProviders, recentWallet, connect, simulate, sign, broadcast } = useShuttle()
|
||||||
|
const [isConnecting, setIsConnecting] = useToggle()
|
||||||
|
const providerId = props.providerId ?? recentWallet?.providerId
|
||||||
|
const isAutoConnect = props.autoConnect
|
||||||
|
|
||||||
|
const handleConnect = async (extensionProviderId: string) => {
|
||||||
|
setIsConnecting(true)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const provider = extensionProviders.find((p) => p.id === providerId)
|
||||||
|
const response =
|
||||||
|
isAutoConnect && provider
|
||||||
|
? await provider.connect({ chainId: currentChainId })
|
||||||
|
: await connect({ extensionProviderId, chainId: currentChainId })
|
||||||
|
const cosmClient = await CosmWasmClient.connect(response.network.rpc)
|
||||||
|
const walletClient: WalletClient = {
|
||||||
|
broadcast,
|
||||||
|
cosmWasmClient: cosmClient,
|
||||||
|
connectedWallet: response,
|
||||||
|
sign,
|
||||||
|
simulate,
|
||||||
|
}
|
||||||
|
setIsConnecting(false)
|
||||||
|
useStore.setState({
|
||||||
|
client: walletClient,
|
||||||
|
address: response.account.address,
|
||||||
|
focusComponent: <WalletFetchBalancesAndAccounts />,
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
setIsConnecting(false)
|
||||||
|
useStore.setState({
|
||||||
|
client: undefined,
|
||||||
|
address: undefined,
|
||||||
|
accounts: null,
|
||||||
|
focusComponent: (
|
||||||
|
<WalletSelect
|
||||||
|
error={{
|
||||||
|
title: 'Failed to connect to wallet',
|
||||||
|
message: mapErrorMessages(extensionProviderId, error.message),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isConnecting || !providerId) return
|
||||||
|
handleConnect(providerId)
|
||||||
|
}, [isConnecting, providerId, handleConnect])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FullOverlayContent
|
||||||
|
title={'Connecting...'}
|
||||||
|
copy={'Unlock your wallet and approve the connection'}
|
||||||
|
>
|
||||||
|
<CircularProgress size={40} />
|
||||||
|
</FullOverlayContent>
|
||||||
|
)
|
||||||
|
}
|
@ -1,23 +1,29 @@
|
|||||||
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
|
|
||||||
import { useShuttle } from '@delphi-labs/shuttle-react'
|
import { useShuttle } from '@delphi-labs/shuttle-react'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import React, { useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import QRCode from 'react-qr-code'
|
import QRCode from 'react-qr-code'
|
||||||
|
|
||||||
import Button from 'components/Button'
|
import Button from 'components/Button'
|
||||||
import { CircularProgress } from 'components/CircularProgress'
|
|
||||||
import FullOverlayContent from 'components/FullOverlayContent'
|
import FullOverlayContent from 'components/FullOverlayContent'
|
||||||
import { ChevronLeft, ChevronRight } from 'components/Icons'
|
import { ChevronLeft, ChevronRight } from 'components/Icons'
|
||||||
import Text from 'components/Text'
|
import Text from 'components/Text'
|
||||||
import WalletFetchBalancesAndAccounts from 'components/Wallet/WalletFetchBalancesAndAccounts'
|
import WalletConnecting from 'components/Wallet/WalletConnecting'
|
||||||
import { CHAINS } from 'constants/chains'
|
import { CHAINS } from 'constants/chains'
|
||||||
import { ENV } from 'constants/env'
|
import { ENV } from 'constants/env'
|
||||||
import { WALLETS } from 'constants/wallets'
|
import { WALLETS } from 'constants/wallets'
|
||||||
import useToggle from 'hooks/useToggle'
|
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
import { WalletID } from 'types/enums/wallet'
|
import { WalletID } from 'types/enums/wallet'
|
||||||
import { isAndroid, isIOS, isMobile } from 'utils/mobile'
|
import { isAndroid, isIOS, isMobile } from 'utils/mobile'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
error?: ErrorObject
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ErrorObject {
|
||||||
|
title: string
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
interface WalletOptionProps {
|
interface WalletOptionProps {
|
||||||
name: string
|
name: string
|
||||||
imageSrc: string
|
imageSrc: string
|
||||||
@ -27,16 +33,6 @@ interface WalletOptionProps {
|
|||||||
const currentChainId = ENV.CHAIN_ID
|
const currentChainId = ENV.CHAIN_ID
|
||||||
const currentChain = CHAINS[currentChainId]
|
const currentChain = CHAINS[currentChainId]
|
||||||
|
|
||||||
const mapErrorMessages = (providerId: string, errorMessage: string) => {
|
|
||||||
if (providerId === 'station') {
|
|
||||||
if (errorMessage.match('Wallet not connected to the network with chainId')) {
|
|
||||||
return `Your wallet is not connected to the correct network. Please switch your wallet to the ${currentChain.name} network`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return errorMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
function WalletOption(props: WalletOptionProps) {
|
function WalletOption(props: WalletOptionProps) {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
@ -57,44 +53,16 @@ function WalletOption(props: WalletOptionProps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function WalletSelect() {
|
export default function WalletSelect(props: Props) {
|
||||||
const { extensionProviders, mobileProviders, connect, mobileConnect, simulate, sign, broadcast } =
|
const { extensionProviders, mobileProviders, mobileConnect } = useShuttle()
|
||||||
useShuttle()
|
|
||||||
const [isConnecting, setIsConnecting] = useToggle(false)
|
|
||||||
const [qrCodeUrl, setQRCodeUrl] = useState('')
|
const [qrCodeUrl, setQRCodeUrl] = useState('')
|
||||||
|
const [error, setError] = useState(props.error)
|
||||||
const sortedExtensionProviders = extensionProviders.sort((a, b) => +b - +a)
|
const sortedExtensionProviders = extensionProviders.sort((a, b) => +b - +a)
|
||||||
|
|
||||||
const handleConnectClick = async (extensionProviderId: string, chainId: string) => {
|
const handleConnectClick = (extensionProviderId: string) => {
|
||||||
setIsConnecting(true)
|
useStore.setState({
|
||||||
|
focusComponent: <WalletConnecting providerId={extensionProviderId} />,
|
||||||
try {
|
})
|
||||||
const response = await connect({ extensionProviderId, chainId })
|
|
||||||
const cosmClient = await CosmWasmClient.connect(response.network.rpc)
|
|
||||||
const walletClient: WalletClient = {
|
|
||||||
broadcast,
|
|
||||||
cosmWasmClient: cosmClient,
|
|
||||||
connectedWallet: response,
|
|
||||||
sign,
|
|
||||||
simulate,
|
|
||||||
}
|
|
||||||
useStore.setState({
|
|
||||||
client: walletClient,
|
|
||||||
address: response.account.address,
|
|
||||||
focusComponent: <WalletFetchBalancesAndAccounts />,
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof Error) {
|
|
||||||
useStore.setState({
|
|
||||||
toast: {
|
|
||||||
isError: true,
|
|
||||||
title: 'Failed to connect to wallet',
|
|
||||||
message: mapErrorMessages(extensionProviderId, error.message),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setIsConnecting(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMobileConnectClick = async (mobileProviderId: string, chainId: string) => {
|
const handleMobileConnectClick = async (mobileProviderId: string, chainId: string) => {
|
||||||
@ -112,33 +80,27 @@ export default function WalletSelect() {
|
|||||||
} else {
|
} else {
|
||||||
window.location.href = urls.androidUrl
|
window.location.href = urls.androidUrl
|
||||||
}
|
}
|
||||||
setIsConnecting(true)
|
|
||||||
} else {
|
} else {
|
||||||
setQRCodeUrl(urls.qrCodeUrl)
|
setQRCodeUrl(urls.qrCodeUrl)
|
||||||
setIsConnecting(false)
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
useStore.setState({
|
setError({ title: 'Failed to connect to wallet', message: error.message })
|
||||||
toast: {
|
|
||||||
isError: true,
|
|
||||||
title: 'Failed to connect to wallet',
|
|
||||||
message: error.message,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isConnecting)
|
useEffect(() => {
|
||||||
return (
|
if (error?.message && error?.title) {
|
||||||
<FullOverlayContent
|
useStore.setState({
|
||||||
title={'Connecting...'}
|
toast: {
|
||||||
copy={'Unlock your wallet and approve the connection'}
|
isError: true,
|
||||||
>
|
title: error.title,
|
||||||
<CircularProgress size={40} />
|
message: error.message,
|
||||||
</FullOverlayContent>
|
},
|
||||||
)
|
})
|
||||||
|
}
|
||||||
|
}, [error?.message, error?.title])
|
||||||
|
|
||||||
if (qrCodeUrl)
|
if (qrCodeUrl)
|
||||||
return (
|
return (
|
||||||
@ -193,7 +155,7 @@ export default function WalletSelect() {
|
|||||||
return (
|
return (
|
||||||
<WalletOption
|
<WalletOption
|
||||||
key={`${walletId}-${network.chainId}`}
|
key={`${walletId}-${network.chainId}`}
|
||||||
handleClick={() => handleConnectClick(walletId, network.chainId)}
|
handleClick={() => handleConnectClick(walletId)}
|
||||||
imageSrc={WALLETS[walletId].imageURL}
|
imageSrc={WALLETS[walletId].imageURL}
|
||||||
name={WALLETS[walletId].name ?? 'Conenct Wallet'}
|
name={WALLETS[walletId].name ?? 'Conenct Wallet'}
|
||||||
/>
|
/>
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
|
|
||||||
import { useShuttle } from '@delphi-labs/shuttle-react'
|
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useLocation, useNavigate, useParams } from 'react-router-dom'
|
import { useLocation, useNavigate, useParams } from 'react-router-dom'
|
||||||
|
|
||||||
import WalletConnectButton from 'components/Wallet/WalletConnectButton'
|
import WalletConnectButton from 'components/Wallet/WalletConnectButton'
|
||||||
import WalletConnectedButton from 'components/Wallet/WalletConnectedButton'
|
import WalletConnectedButton from 'components/Wallet/WalletConnectedButton'
|
||||||
|
import WalletConnecting from 'components/Wallet/WalletConnecting'
|
||||||
import useCurrentWallet from 'hooks/useCurrentWallet'
|
import useCurrentWallet from 'hooks/useCurrentWallet'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
import { getPage, getRoute } from 'utils/route'
|
import { getPage, getRoute } from 'utils/route'
|
||||||
@ -14,39 +13,22 @@ export default function Wallet() {
|
|||||||
const { address: addressInUrl } = useParams()
|
const { address: addressInUrl } = useParams()
|
||||||
const { pathname } = useLocation()
|
const { pathname } = useLocation()
|
||||||
const currentWallet = useCurrentWallet()
|
const currentWallet = useCurrentWallet()
|
||||||
const { simulate, sign, broadcast } = useShuttle()
|
|
||||||
const address = useStore((s) => s.address)
|
const address = useStore((s) => s.address)
|
||||||
const client = useStore((s) => s.client)
|
const client = useStore((s) => s.client)
|
||||||
|
|
||||||
// Set connection status
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const isConnected = !!currentWallet
|
if (!currentWallet) {
|
||||||
useStore.setState(
|
useStore.setState({ address: undefined, accounts: null, client: undefined })
|
||||||
isConnected
|
return
|
||||||
? {
|
|
||||||
address: currentWallet.account.address,
|
|
||||||
}
|
|
||||||
: { address: undefined, accounts: null, client: undefined },
|
|
||||||
)
|
|
||||||
}, [currentWallet])
|
|
||||||
|
|
||||||
// Set the client
|
|
||||||
useEffect(() => {
|
|
||||||
async function getCosmWasmClient() {
|
|
||||||
if (client || !currentWallet) return
|
|
||||||
const cosmClient = await CosmWasmClient.connect(currentWallet.network.rpc)
|
|
||||||
const walletClient: WalletClient = {
|
|
||||||
broadcast,
|
|
||||||
cosmWasmClient: cosmClient,
|
|
||||||
connectedWallet: currentWallet,
|
|
||||||
sign,
|
|
||||||
simulate,
|
|
||||||
}
|
|
||||||
useStore.setState({ client: walletClient })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getCosmWasmClient()
|
if (client) {
|
||||||
}, [currentWallet, address, simulate, sign, broadcast, client])
|
if (currentWallet.account.address !== address)
|
||||||
|
useStore.setState({ address: currentWallet.account.address })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
useStore.setState({ focusComponent: <WalletConnecting autoConnect /> })
|
||||||
|
}, [currentWallet, client, address])
|
||||||
|
|
||||||
// Redirect when switching wallets or on first connection
|
// Redirect when switching wallets or on first connection
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
Loading…
Reference in New Issue
Block a user