Skip to content

Commit

Permalink
Merge branch 'main' into deployments
Browse files Browse the repository at this point in the history
  • Loading branch information
solanoepalacio committed Apr 2, 2024
2 parents 31dbdf5 + dfb34a7 commit a4a12b0
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 2 deletions.
16 changes: 15 additions & 1 deletion frontend/components/wallets/EVM.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ import { CoinbaseWalletConnector } from 'wagmi/connectors/coinbaseWallet'
import { MetaMaskConnector } from 'wagmi/connectors/metaMask'
import { WalletConnectConnector } from 'wagmi/connectors/walletConnect'
import { InjectedConnector } from 'wagmi/connectors/injected'
import { PhantomConnector } from './phantomConnector'

import coinbase from '@images/coinbase.svg'
import walletConnect from '@images/wallet-connect.svg'
import metamask from '@images/metamask.svg'
import okx from '@images/okx.svg'
import phantom from '@images/phantom.svg'

import { getInjectiveAddress } from '../../utils/getInjectiveAddress'

Expand Down Expand Up @@ -54,12 +56,23 @@ const config = createConfig({
showQrModal: true,
},
}),
new PhantomConnector({
chains,
options: {
name: 'Phantom',
},
}),
],
publicClient,
webSocketPublicClient,
})

type WalletIds = 'metaMask' | 'coinbaseWallet' | 'walletConnect' | 'injected'
type WalletIds =
| 'metaMask'
| 'coinbaseWallet'
| 'walletConnect'
| 'injected'
| 'phantom'

type EVMWalletProviderProps = {
children: ReactNode
Expand Down Expand Up @@ -153,6 +166,7 @@ function getIcon(id: WalletIds | undefined) {
if (id === undefined) return undefined
if (id === 'metaMask') return metamask
if (id === 'coinbaseWallet') return coinbase
if (id === 'phantom') return phantom
if (id === 'injected') return okx
return walletConnect
}
15 changes: 14 additions & 1 deletion frontend/components/wallets/Solana.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,20 @@ export function SolanaWalletProvider({

return (
<ConnectionProvider endpoint={endpoint}>
<WalletProvider wallets={wallets} autoConnect>
<WalletProvider
onError={(e) => {
if (
e?.error?.code === -32603 &&
e?.error?.message === 'Unexpected error'
) {
alert(
`An unexpected error happened.\n\nMake sure that the wallet that you selected it's connected to your Solana account and reload the page.`
)
}
}}
wallets={wallets}
autoConnect
>
{children}
</WalletProvider>
</ConnectionProvider>
Expand Down
140 changes: 140 additions & 0 deletions frontend/components/wallets/phantomConnector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { InjectedConnector } from 'wagmi/connectors/injected'
import { WindowProvider, ConnectorNotFoundError } from 'wagmi'
import {
ProviderRpcError,
ResourceNotFoundRpcError,
UserRejectedRequestError,
getAddress,
} from 'viem'
import type { Address } from 'abitype'
import type { Chain } from '@wagmi/core/chains'

type InjectedConnectorOptions = {
/** Name of connector */
name?: string | ((detectedName: string | string[]) => string)
/**
* [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) Ethereum Provider to target
*
* @default
* () => typeof window !== 'undefined' ? window.ethereum : undefined
*/
getProvider?: () => WindowProvider | undefined
/**
* MetaMask and other injected providers do not support programmatic disconnect.
* This flag simulates the disconnect behavior by keeping track of connection status in storage. See [GitHub issue](https://github.com/MetaMask/metamask-extension/issues/10353) for more info.
* @default true
*/
shimDisconnect?: boolean
}

declare global {
interface Window {
phantom: {
ethereum: WindowProvider
}
}
}

export class PhantomConnector extends InjectedConnector {
readonly id = 'phantom'

protected shimDisconnectKey = `${this.id}.shimDisconnect`

constructor({
chains,
options: options_,
}: {
chains?: Chain[]
options?: InjectedConnectorOptions
} = {}) {
const options = {
name: 'Phantom',
shimDisconnect: true,
getProvider() {
function getReady(ethereum?: WindowProvider) {
const isPhantom = !!ethereum?.isPhantom
if (!isPhantom) return
return ethereum
}

if (typeof window === 'undefined') return
const ethereum = window?.phantom?.ethereum as WindowProvider | undefined
if (ethereum?.providers) return ethereum.providers.find(getReady)
return getReady(ethereum)
},
...options_,
}
super({ chains, options })
}

async connect({ chainId }: { chainId?: number } = {}) {
try {
const provider = await this.getProvider()
if (!provider) throw new ConnectorNotFoundError()

if (provider.on) {
provider.on('accountsChanged', this.onAccountsChanged)
provider.on('chainChanged', this.onChainChanged)
provider.on('disconnect', this.onDisconnect)
}

this.emit('message', { type: 'connecting' })

// Attempt to show wallet select prompt with `wallet_requestPermissions` when
// `shimDisconnect` is active and account is in disconnected state (flag in storage)
let account: Address | null = null
if (
this.options?.shimDisconnect &&
!this.storage?.getItem(this.shimDisconnectKey)
) {
account = await this.getAccount().catch(() => null)
const isConnected = !!account
if (isConnected)
// Attempt to show another prompt for selecting wallet if already connected
try {
await provider.request({
method: 'wallet_requestPermissions',
params: [{ eth_accounts: {} }],
})
// User may have selected a different account so we will need to revalidate here.
account = await this.getAccount()
} catch (error) {
if (this.isUserRejectedRequestError(error))
throw new UserRejectedRequestError(error as Error)
if (
(error as ProviderRpcError).code ===
new ResourceNotFoundRpcError(error as ProviderRpcError).code
)
throw error
}
}

if (!account) {
const accounts = await provider.request({
method: 'eth_requestAccounts',
})
account = getAddress(accounts[0] as string)
}

// Switch to chain if provided
let id = await this.getChainId()
let unsupported = this.isChainUnsupported(id)
if (chainId && id !== chainId) {
const chain = await this.switchChain(chainId)
id = chain.id
unsupported = this.isChainUnsupported(id)
}

if (this.options?.shimDisconnect)
this.storage?.setItem(this.shimDisconnectKey, true)

return { account, chain: { id, unsupported }, provider }
} catch (error) {
if (this.isUserRejectedRequestError(error))
throw new UserRejectedRequestError(error as Error)
if ((error as ProviderRpcError).code === -32002)
throw new ResourceNotFoundRpcError(error as ProviderRpcError)
throw error
}
}
}
89 changes: 89 additions & 0 deletions frontend/images/phantom.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit a4a12b0

Please sign in to comment.