Skip to content

Commit

Permalink
[skip ci] wip: phantom wire-up
Browse files Browse the repository at this point in the history
  • Loading branch information
gomesalexandre committed Sep 18, 2024
1 parent 90454aa commit b9cc0c3
Show file tree
Hide file tree
Showing 15 changed files with 343 additions and 94 deletions.
2 changes: 1 addition & 1 deletion .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
nodeLinker: node-modules

npmRegistryServer: "https://registry.npmjs.org"
npmRegistryServer: "http://127.0.0.1:4873/"

plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
Expand Down
27 changes: 14 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,19 +91,20 @@
"@shapeshiftoss/caip": "workspace:^",
"@shapeshiftoss/chain-adapters": "workspace:^",
"@shapeshiftoss/errors": "workspace:^",
"@shapeshiftoss/hdwallet-coinbase": "1.55.4",
"@shapeshiftoss/hdwallet-core": "1.55.4",
"@shapeshiftoss/hdwallet-keepkey": "1.55.4",
"@shapeshiftoss/hdwallet-keepkey-webusb": "1.55.4",
"@shapeshiftoss/hdwallet-keplr": "1.55.4",
"@shapeshiftoss/hdwallet-ledger": "1.55.4",
"@shapeshiftoss/hdwallet-ledger-webusb": "1.55.4",
"@shapeshiftoss/hdwallet-metamask": "1.55.4",
"@shapeshiftoss/hdwallet-native": "1.55.4",
"@shapeshiftoss/hdwallet-native-vault": "1.55.4",
"@shapeshiftoss/hdwallet-shapeshift-multichain": "1.55.4",
"@shapeshiftoss/hdwallet-walletconnectv2": "1.55.4",
"@shapeshiftoss/hdwallet-xdefi": "1.55.4",
"@shapeshiftoss/hdwallet-coinbase": "1.55.5-phantom.6",
"@shapeshiftoss/hdwallet-core": "1.55.5-phantom.6",
"@shapeshiftoss/hdwallet-keepkey": "1.55.5-phantom.6",
"@shapeshiftoss/hdwallet-keepkey-webusb": "1.55.5-phantom.6",
"@shapeshiftoss/hdwallet-keplr": "1.55.5-phantom.6",
"@shapeshiftoss/hdwallet-ledger": "1.55.5-phantom.6",
"@shapeshiftoss/hdwallet-ledger-webusb": "1.55.5-phantom.6",
"@shapeshiftoss/hdwallet-metamask": "1.55.5-phantom.6",
"@shapeshiftoss/hdwallet-native": "1.55.5-phantom.6",
"@shapeshiftoss/hdwallet-native-vault": "1.55.5-phantom.6",
"@shapeshiftoss/hdwallet-phantom": "1.55.5-phantom.6",
"@shapeshiftoss/hdwallet-shapeshift-multichain": "1.55.5-phantom.6",
"@shapeshiftoss/hdwallet-walletconnectv2": "1.55.5-phantom.6",
"@shapeshiftoss/hdwallet-xdefi": "1.55.5-phantom.6",
"@shapeshiftoss/swapper": "workspace:^",
"@shapeshiftoss/types": "workspace:^",
"@shapeshiftoss/unchained-client": "workspace:^",
Expand Down
16 changes: 16 additions & 0 deletions src/assets/translations/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -1525,6 +1525,22 @@
"button": "Open"
}
},
"phantom": {
"errors": {
"unknown": "An unexpected error occurred communicating with Phantom",
"connectFailure": "Unable to connect Phantom wallet",
"multipleWallets": "Detected Ethereum provider is not Phantom. Do you have multiple wallets installed?"
},
"connect": {
"header": "Pair Phantom",
"body": "Click Pair and login to Phantom from the popup window",
"button": "Pair"
},
"failure": {
"header": "Error",
"body": "Unable to connect Phantom wallet"
}
},
"metaMaskSnap": {
"title": "Multichain support is now available for MetaMask!",
"subtitle": "Add the Multichain Snap on Metamask to send, receive, track, trade, and earn with the following chains:",
Expand Down
20 changes: 20 additions & 0 deletions src/components/Icons/PhantomIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createIcon } from '@chakra-ui/react'

export const PhantomIcon = createIcon({
displayName: 'PhantomIcon',
path: (
<svg
width='593'
height='493'
viewBox='0 0 593 493'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<path
d='M70.0546 493C145.604 493 202.38 427.297 236.263 375.378C232.142 386.865 229.852 398.351 229.852 409.378C229.852 439.703 247.252 461.297 281.592 461.297C328.753 461.297 379.119 419.946 405.218 375.378C403.386 381.811 402.471 387.784 402.471 393.297C402.471 414.432 414.375 427.757 438.643 427.757C515.108 427.757 592.03 292.216 592.03 173.676C592.03 81.3243 545.327 0 428.112 0C222.069 0 0 251.784 0 414.432C0 478.297 34.3405 493 70.0546 493ZM357.141 163.568C357.141 140.595 369.962 124.514 388.734 124.514C407.049 124.514 419.87 140.595 419.87 163.568C419.87 186.541 407.049 203.081 388.734 203.081C369.962 203.081 357.141 186.541 357.141 163.568ZM455.126 163.568C455.126 140.595 467.947 124.514 486.719 124.514C505.034 124.514 517.855 140.595 517.855 163.568C517.855 186.541 505.034 203.081 486.719 203.081C467.947 203.081 455.126 186.541 455.126 163.568Z'
fill='#AB9FF2'
/>
</svg>
),
viewBox: '0 0 593 493',
})
1 change: 1 addition & 0 deletions src/context/WalletProvider/KeyManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export enum KeyManager {
Native = 'native',
KeepKey = 'keepkey',
MetaMask = 'metamask',
Phantom = 'phantom',
Demo = 'demo',
XDefi = 'xdefi',
Keplr = 'keplr',
Expand Down
83 changes: 83 additions & 0 deletions src/context/WalletProvider/Phantom/components/Connect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { useCallback, useState } from 'react'
import type { RouteComponentProps } from 'react-router-dom'
import type { ActionTypes } from 'context/WalletProvider/actions'
import { WalletActions } from 'context/WalletProvider/actions'
import { KeyManager } from 'context/WalletProvider/KeyManager'
import { useLocalWallet } from 'context/WalletProvider/local-wallet'
import { removeAccountsAndChainListeners } from 'context/WalletProvider/WalletProvider'
import { useWallet } from 'hooks/useWallet/useWallet'

import { ConnectModal } from '../../components/ConnectModal'
import type { LocationState } from '../../NativeWallet/types'
import { PhantomConfig } from '../config'

export interface PhantomSetupProps
extends RouteComponentProps<
{},
any, // history
LocationState
> {
dispatch: React.Dispatch<ActionTypes>
}

export const PhantomConnect = ({ history }: PhantomSetupProps) => {
const { dispatch, getAdapter, onProviderChange } = useWallet()
const localWallet = useLocalWallet()
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)

const setErrorLoading = useCallback((e: string | null) => {
setError(e)
setLoading(false)
}, [])

const pairDevice = useCallback(async () => {
setError(null)
setLoading(true)

const adapter = await getAdapter(KeyManager.Phantom)
if (adapter) {
try {
// Remove all provider event listeners from previously connected wallets
await removeAccountsAndChainListeners()

const wallet = await adapter.pairDevice()
if (!wallet) {
setErrorLoading('walletProvider.errors.walletNotFound')
throw new Error('Call to hdwallet-phantom::pairDevice returned null or undefined')
}

await onProviderChange(KeyManager.Phantom, wallet)

const { name, icon } = PhantomConfig
const deviceId = await wallet.getDeviceID()
const isLocked = await wallet.isLocked()
await wallet.initialize()
dispatch({
type: WalletActions.SET_WALLET,
payload: { wallet, name, icon, deviceId, connectedType: KeyManager.Phantom },
})
dispatch({ type: WalletActions.SET_IS_CONNECTED, payload: true })
dispatch({ type: WalletActions.SET_IS_LOCKED, payload: isLocked })
localWallet.setLocalWalletTypeAndDeviceId(KeyManager.Phantom, deviceId)
dispatch({ type: WalletActions.SET_WALLET_MODAL, payload: false })
} catch (e: any) {
console.error(e, 'Phantom Connect: There was an error initializing the wallet')
setErrorLoading(e.message)
history.push('/phantom/failure')
}
}
setLoading(false)
}, [dispatch, getAdapter, history, localWallet, onProviderChange, setErrorLoading])

return (
<ConnectModal
headerText={'walletProvider.phantom.connect.header'}
bodyText={'walletProvider.phantom.connect.body'}
buttonText={'walletProvider.phantom.connect.button'}
onPairDeviceClick={pairDevice}
loading={loading}
error={error}
/>
)
}
10 changes: 10 additions & 0 deletions src/context/WalletProvider/Phantom/components/Failure.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { FailureModal } from 'context/WalletProvider/components/FailureModal'

export const PhantomFailure = () => {
return (
<FailureModal
headerText={'walletProvider.metaMask.failure.header'}
bodyText={'walletProvider.metaMask.failure.body'}
/>
)
}
21 changes: 21 additions & 0 deletions src/context/WalletProvider/Phantom/components/SnapInstall.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useCallback } from 'react'
import { SnapContent } from 'components/Modals/Snaps/SnapContent'
import { WalletActions } from 'context/WalletProvider/actions'
import { useWallet } from 'hooks/useWallet/useWallet'

export const SnapInstall = () => {
const { dispatch } = useWallet()

const handleClose = useCallback(() => {
dispatch({ type: WalletActions.SET_WALLET_MODAL, payload: false })
}, [dispatch])

return (
<SnapContent
onClose={handleClose}
// If we land here, we don't care about versioning, the user does *not* have the snap installed yet
isCorrectVersion
isSnapInstalled={false}
/>
)
}
17 changes: 17 additions & 0 deletions src/context/WalletProvider/Phantom/components/SnapUpdate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useCallback } from 'react'
import { SnapContent } from 'components/Modals/Snaps/SnapContent'
import { WalletActions } from 'context/WalletProvider/actions'
import { useWallet } from 'hooks/useWallet/useWallet'

export const SnapUpdate = () => {
const { dispatch } = useWallet()

const handleClose = useCallback(() => {
dispatch({ type: WalletActions.SET_WALLET_MODAL, payload: false })
}, [dispatch])

return (
// If we land here, we know the version is incorrect
<SnapContent onClose={handleClose} isCorrectVersion={false} isSnapInstalled={true} />
)
}
16 changes: 16 additions & 0 deletions src/context/WalletProvider/Phantom/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { PhantomAdapter } from '@shapeshiftoss/hdwallet-phantom'
import { PhantomIcon } from 'components/Icons/PhantomIcon'
import type { SupportedWalletInfo } from 'context/WalletProvider/config'

type PhantomConfigType = Omit<SupportedWalletInfo<typeof PhantomAdapter>, 'routes'>

export const PhantomConfig: PhantomConfigType = {
adapters: [
{
loadAdapter: () => import('@shapeshiftoss/hdwallet-phantom').then(m => m.PhantomAdapter),
},
],
supportsMobile: 'browser',
icon: PhantomIcon,
name: 'Phantom',
}
1 change: 1 addition & 0 deletions src/context/WalletProvider/WalletProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export type MetaMaskLikeProvider = BrowserProvider
export type KeyManagerWithProvider =
| KeyManager.XDefi
| KeyManager.MetaMask
| KeyManager.Phantom
| KeyManager.WalletConnectV2
| KeyManager.Coinbase

Expand Down
23 changes: 23 additions & 0 deletions src/context/WalletProvider/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { KeplrAdapter } from '@shapeshiftoss/hdwallet-keplr'
import type { WebUSBLedgerAdapter as LedgerAdapter } from '@shapeshiftoss/hdwallet-ledger-webusb'
import type { MetaMaskAdapter } from '@shapeshiftoss/hdwallet-metamask'
import type { NativeAdapter } from '@shapeshiftoss/hdwallet-native'
import type { PhantomAdapter } from '@shapeshiftoss/hdwallet-phantom'
import type { MetaMaskAdapter as MetaMaskMultiChainAdapter } from '@shapeshiftoss/hdwallet-shapeshift-multichain'
import type { WalletConnectV2Adapter } from '@shapeshiftoss/hdwallet-walletconnectv2'
import type { XDEFIAdapter } from '@shapeshiftoss/hdwallet-xdefi'
Expand All @@ -26,6 +27,7 @@ import { LedgerConfig } from './Ledger/config'
import { MetaMaskConfig } from './MetaMask/config'
import { MobileConfig } from './MobileWallet/config'
import { NativeConfig } from './NativeWallet/config'
import { PhantomConfig } from './Phantom/config'
import { KeepKeyRoutes } from './routes'
import { NativeWalletRoutes } from './types'
import { WalletConnectV2Config } from './WalletConnectV2/config'
Expand Down Expand Up @@ -238,6 +240,18 @@ const MetaMaskFailure = lazy(() =>
default: MetaMaskFailure,
})),
)

const PhantomConnect = lazy(() =>
import('./Phantom/components/Connect').then(({ PhantomConnect }) => ({
default: PhantomConnect,
})),
)
const PhantomFailure = lazy(() =>
import('./Phantom/components/Failure').then(({ PhantomFailure }) => ({
default: PhantomFailure,
})),
)

const MetaMaskMenu = lazy(() =>
import('./MetaMask/components/MetaMaskMenu').then(({ MetaMaskMenu }) => ({
default: MetaMaskMenu,
Expand Down Expand Up @@ -325,6 +339,7 @@ export type SupportedWalletInfoByKeyManager = {
[KeyManager.KeepKey]: SupportedWalletInfo<typeof WebUSBKeepKeyAdapter | typeof KkRestAdapter>
[KeyManager.Keplr]: SupportedWalletInfo<typeof KeplrAdapter>
[KeyManager.Ledger]: SupportedWalletInfo<typeof LedgerAdapter>
[KeyManager.Phantom]: SupportedWalletInfo<typeof PhantomAdapter>
[KeyManager.MetaMask]: SupportedWalletInfo<
typeof MetaMaskAdapter | typeof MetaMaskMultiChainAdapter
>
Expand Down Expand Up @@ -404,6 +419,14 @@ export const SUPPORTED_WALLETS: SupportedWalletInfoByKeyManager = {
],
connectedMenuComponent: MetaMaskMenu,
},
[KeyManager.Phantom]: {
...PhantomConfig,
routes: [
{ path: '/phantom/connect', component: PhantomConnect },
{ path: '/phantom/failure', component: PhantomFailure },
],
},

[KeyManager.XDefi]: {
...XDEFIConfig,
routes: [
Expand Down
2 changes: 2 additions & 0 deletions src/context/WalletProvider/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { KeplrAdapter } from '@shapeshiftoss/hdwallet-keplr'
import type { WebUSBLedgerAdapter } from '@shapeshiftoss/hdwallet-ledger-webusb'
import type { MetaMaskAdapter } from '@shapeshiftoss/hdwallet-metamask'
import type { NativeAdapter } from '@shapeshiftoss/hdwallet-native'
import type { PhantomAdapter } from '@shapeshiftoss/hdwallet-phantom'
import type { WalletConnectV2Adapter } from '@shapeshiftoss/hdwallet-walletconnectv2'
import type { XDEFIAdapter } from '@shapeshiftoss/hdwallet-xdefi'

Expand All @@ -19,6 +20,7 @@ export type AdaptersByKeyManager = {
[KeyManager.Keplr]: KeplrAdapter
[KeyManager.WalletConnectV2]: WalletConnectV2Adapter
[KeyManager.MetaMask]: MetaMaskAdapter
[KeyManager.Phantom]: PhantomAdapter
[KeyManager.Coinbase]: CoinbaseAdapter
[KeyManager.XDefi]: XDEFIAdapter
}
Expand Down
15 changes: 14 additions & 1 deletion src/lib/account/utxo.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { toAccountId } from '@shapeshiftoss/caip'
import { utxoAccountParams, utxoChainIds } from '@shapeshiftoss/chain-adapters'
import { supportsBTC } from '@shapeshiftoss/hdwallet-core'
import { PhantomHDWallet } from '@shapeshiftoss/hdwallet-phantom'
import { MetaMaskShapeShiftMultiChainHDWallet } from '@shapeshiftoss/hdwallet-shapeshift-multichain'
import type { AccountMetadataById, UtxoChainId } from '@shapeshiftoss/types'
import { UtxoAccountType } from '@shapeshiftoss/types'
Expand All @@ -23,8 +24,20 @@ export const deriveUtxoAccountIdsAndMetadata: DeriveAccountIdsAndMetadata = asyn
// MetaMask snaps adapter only supports legacy for BTC and LTC
supportedAccountTypes = [UtxoAccountType.P2pkh]
}
if (wallet instanceof PhantomHDWallet) {
// Phantom supposedly supports more script types, but only supports Segwit Native (bech32 addresses) for now
supportedAccountTypes = [UtxoAccountType.SegwitNative]
}
for (const accountType of supportedAccountTypes) {
const { xpub: pubkey } = await adapter.getPublicKey(wallet, accountNumber, accountType)
const { xpub: pubkey } = await adapter
.getPublicKey(wallet, accountNumber, accountType)
.catch(e => {
console.error(
`Error getting pubkey for chainId: ${chainId} accountType: ${accountType} accountNumber: ${accountNumber}`,
e,
)
return { xpub: null }
})

if (!pubkey) continue

Expand Down
Loading

0 comments on commit b9cc0c3

Please sign in to comment.