Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/wc error notification #805

Merged
merged 17 commits into from
Sep 4, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -542,9 +542,7 @@ export class WalletConnectCommunicationClient extends CommunicationClient {
const signClient = await this.getSignClient()

if (!signClient) {
const fun = this.eventHandlers.get(ClientEvents.CLOSE_ALERT)
fun && fun()
return
throw new Error('Failed to connect to the relayer.')
}

const lastIndex = signClient.session.keys.length - 1
Expand Down Expand Up @@ -586,7 +584,11 @@ export class WalletConnectCommunicationClient extends CommunicationClient {
}
}

const { uri, approval } = await signClient.connect(connectParams)
const { uri, approval } = await signClient.connect(connectParams).catch((error) => {
logger.error(`Init error: ${error.message}`)
localStorage && localStorage.setItem(StorageKey.WC_INIT_ERROR, error.message)
throw new Error(error.message)
})

// Extract topic from uri. Format is wc:topic@2...
const topic = getStringBetween(uri, ':', '@')
Expand Down Expand Up @@ -1312,13 +1314,34 @@ export class WalletConnectCommunicationClient extends CommunicationClient {
return this.session
}

private async tryConnectToRelayer() {
const urls = new Set([
this.wcOptions.opts.relayUrl,
undefined,
'wss://relay.walletconnect.com',
'wss://relay.walletconnect.org'
])
const errMessages = new Set()

for (const relayUrl of urls) {
try {
return await Client.init({ ...this.wcOptions.opts, relayUrl })
} catch (err: any) {
errMessages.add(err.message)
logger.warn(`Failed to connect to ${relayUrl}: ${err.message}`)
}
}
throw new Error(`Failed to connect to relayer: ${Array.from(errMessages).join(',')}`)
}

private async getSignClient(): Promise<Client | undefined> {
if (this.signClient === undefined) {
try {
this.signClient = await Client.init(this.wcOptions.opts)
this.signClient = await this.tryConnectToRelayer()
this.subscribeToSessionEvents(this.signClient)
} catch (error: any) {
logger.error(error.message)
localStorage && localStorage.setItem(StorageKey.WC_INIT_ERROR, error.message)
return undefined
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/beacon-types/src/types/storage/StorageKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export enum StorageKey {
MULTI_NODE_SETUP_DONE = 'beacon:multi-node-setup',
USER_ID = 'beacon:user-id',
ENABLE_METRICS = 'beacon:enable_metrics',
WC_INIT_ERROR = 'beacon:wc-init-error',
WC_2_CORE_PAIRING = 'wc@2:core:0.3:pairing',
WC_2_CLIENT_SESSION = 'wc@2:client:0.3:session',
WC_2_CORE_KEYCHAIN = 'wc@2:core:0.3:keychain',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const defaultValues: StorageKeyReturnDefaults = {
[StorageKey.WC_2_CLIENT_SESSION]: undefined,
[StorageKey.USER_ID]: undefined,
[StorageKey.ENABLE_METRICS]: undefined,
[StorageKey.WC_INIT_ERROR]: undefined,
[StorageKey.WC_2_CORE_PAIRING]: undefined,
[StorageKey.WC_2_CORE_KEYCHAIN]: undefined,
[StorageKey.WC_2_CORE_MESSAGES]: undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface StorageKeyReturnType {
[StorageKey.MULTI_NODE_SETUP_DONE]: boolean | undefined
[StorageKey.USER_ID]: string | undefined
[StorageKey.ENABLE_METRICS]: boolean | undefined
[StorageKey.WC_INIT_ERROR]: string | undefined
[StorageKey.WC_2_CLIENT_SESSION]: string | undefined
[StorageKey.WC_2_CORE_PAIRING]: string | undefined
[StorageKey.WC_2_CORE_KEYCHAIN]: string | undefined
Expand Down
22 changes: 0 additions & 22 deletions packages/beacon-ui/src/components/qr/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,28 +75,6 @@ const QR: Component<QRProps> = (props: QRProps) => {
{`Scan QR code with a ${
props.isWalletConnect ? 'WalletConnect' : 'Beacon'
}-compatible wallet.`}
{props.onClickLearnMore && (
<span
class="qr-more-info"
onClick={() => {
if (props.onClickLearnMore) props.onClickLearnMore()
}}
>
Learn more
</span>
)}
</span>
)}

{!props.isMobile && props.onClickLearnMore && (
<span
style={{ 'margin-top': 'auto' }}
class="qr-more-info"
onClick={() => {
if (props.onClickLearnMore) props.onClickLearnMore()
}}
>
Learn more
</span>
)}
</div>
Expand Down
194 changes: 114 additions & 80 deletions packages/beacon-ui/src/ui/alert/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ const [currentInfo, setCurrentInfo] = createSignal<
const [analytics, setAnalytics] = createSignal<AnalyticsInterface | undefined>(undefined)
const [displayQrExtra, setDisplayQrExtra] = createSignal<boolean>(false)
const [pairingExpired, setPairingExpired] = createSignal(false)
const [isWCWorking, setIsWCWorking] = createSignal(true)
type VoidFunction = () => void
let dispose: null | VoidFunction = null

Expand Down Expand Up @@ -451,21 +452,33 @@ const openAlert = async (config: AlertConfig): Promise<string> => {

// check whether the uri contains a valid symmetric key or not
if (!parseUri(uri).symKey) {
handleCloseAlert()
setTimeout(
() =>
openAlert({
title: 'Error',
body: 'Network error occurred. Please check your internet connection.'
}),
500
)
return ''
setIsWCWorking(false)
}

return uri
}

const generateWCError = (title: string) => {
const errorMessage = localStorage ? localStorage.getItem(StorageKey.WC_INIT_ERROR) : undefined
const description: any = (
<>
<h3 style={{ color: '#FF4136', margin: '0.6px' }}>A network error occurred.</h3>
<h4>
This issue does not concern your wallet or dApp. If the problem persists, please report
it to the Beacon team{' '}
<span
style={{ 'text-decoration': 'underline', color: '#007bff', cursor: 'pointer' }}
onClick={() => setCurrentInfo('help')}
>
here
</span>
</h4>
{errorMessage && <span>{errorMessage}</span>}
</>
)
return <Info title={title} description={description} border />
}

const handleNewTab = async (config: AlertConfig, wallet?: MergedWallet) => {
if (!wallet) {
return
Expand Down Expand Up @@ -594,6 +607,13 @@ const openAlert = async (config: AlertConfig): Promise<string> => {
if (wallet && wallet.supportedInteractionStandards?.includes('wallet_connect')) {
const uri = await generateLink()

if (!uri && wallet?.name.toLowerCase().includes('kukai')) {
setCodeQR('error')
setInstallState(wallet)
setIsLoading(false)
return
}

if (uri) {
if (_isMobileOS && wallet.types.includes('ios') && wallet.types.length === 1) {
handleDeepLinking(wallet, uri)
Expand Down Expand Up @@ -746,17 +766,25 @@ const openAlert = async (config: AlertConfig): Promise<string> => {
icon: currentWallet()?.image
})
)
const isConnected =
!currentWallet()?.supportedInteractionStandards?.includes('wallet_connect') || isWCWorking()
return (
<QR
isWalletConnect={
currentWallet()?.supportedInteractionStandards?.includes('wallet_connect') || false
}
isMobile={isMobile}
walletName={currentWallet()?.name || 'AirGap'}
code={codeQR()}
onClickLearnMore={handleClickLearnMore}
onClickQrCode={handleClickQrCode}
/>
<>
{isConnected ? (
<QR
isWalletConnect={
currentWallet()?.supportedInteractionStandards?.includes('wallet_connect') || false
}
isMobile={isMobile}
walletName={currentWallet()?.name || 'AirGap'}
code={codeQR()}
onClickLearnMore={handleClickLearnMore}
onClickQrCode={handleClickQrCode}
/>
) : (
generateWCError(`Connect with ${currentWallet()?.name} Mobile`)
)}
</>
)
}

Expand Down Expand Up @@ -878,72 +906,78 @@ const openAlert = async (config: AlertConfig): Promise<string> => {
(currentWallet()?.types.length as number) <= 1 && (
<QRCode isMobile={true} />
)}
{isMobile() && currentWallet()?.types.includes('ios') && (
<Info
border
title={`Connect with ${currentWallet()?.name} Mobile`}
description={''}
buttons={[
{
label: 'Use App',
type: 'primary',
onClick: async () => {
const wallet = currentWallet()

if (!wallet) {
return
}
{isMobile() &&
currentWallet()?.types.includes('ios') &&
(!currentWallet()?.supportedInteractionStandards?.includes(
'wallet_connect'
) || isWCWorking() ? (
<Info
border
title={`Connect with ${currentWallet()?.name} Mobile`}
description={''}
buttons={[
{
label: 'Use App',
type: 'primary',
onClick: async () => {
const wallet = currentWallet()

if (!wallet) {
return
}

let syncCode = ''
if (
wallet.supportedInteractionStandards?.includes('wallet_connect')
) {
syncCode = (await wcPayload)?.uri ?? ''
} else {
syncCode = await new Serializer().serialize(await p2pPayload)
let syncCode = ''
if (
wallet.supportedInteractionStandards?.includes('wallet_connect')
) {
syncCode = (await wcPayload)?.uri ?? ''
} else {
syncCode = await new Serializer().serialize(await p2pPayload)
}
handleDeepLinking(wallet, syncCode)
}
handleDeepLinking(wallet, syncCode)
}
]}
downloadLink={
currentWallet()?.name.toLowerCase().includes('kukai') && isIOS(window)
? {
label: 'Get Kukai Mobile >',
url: 'https://ios.kukai.app'
}
: undefined
}
]}
downloadLink={
currentWallet()?.name.toLowerCase().includes('kukai') && isIOS(window)
? {
label: 'Get Kukai Mobile >',
url: 'https://ios.kukai.app'
}
: undefined
}
onShowQRCodeClick={async () => {
const syncCode =
currentWallet()?.supportedInteractionStandards?.includes(
'wallet_connect'
)
? (await wcPayload)?.uri ?? ''
: await new Serializer().serialize(await p2pPayload)

const wallet = currentWallet()

if (!syncCode.length || !wallet) {
handleCloseAlert()
return
}
onShowQRCodeClick={async () => {
const syncCode =
currentWallet()?.supportedInteractionStandards?.includes(
'wallet_connect'
)
? (await wcPayload)?.uri ?? ''
: await new Serializer().serialize(await p2pPayload)

const wallet = currentWallet()

if (!syncCode.length || !wallet) {
handleCloseAlert()
return
}

if (
_isMobileOS &&
wallet.types.includes('ios') &&
wallet.types.length === 1
) {
handleDeepLinking(wallet, syncCode)
} else {
setCodeQR(syncCode)
}
if (
_isMobileOS &&
wallet.types.includes('ios') &&
wallet.types.length === 1
) {
handleDeepLinking(wallet, syncCode)
} else {
setCodeQR(syncCode)
}

setCurrentInfo('qr')
setDisplayQrExtra(true)
}}
/>
)}
setCurrentInfo('qr')
setDisplayQrExtra(true)
}}
/>
) : (
generateWCError(`Connect with ${currentWallet()?.name} Mobile`)
))}
</div>
)}
{currentInfo() === 'qr' && (
Expand Down
Loading