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: phantom wire-up #7766

Open
wants to merge 29 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b9cc0c3
[skip ci] wip: phantom wire-up
gomesalexandre Sep 18, 2024
b2b224d
feat: latest bump
gomesalexandre Sep 19, 2024
31cb2d9
Merge branch 'develop' into feat_phantom_wire_up
gomesalexandre Sep 19, 2024
d917951
fix: bech32 prefix checks for account label/ AccountDropdown
gomesalexandre Sep 19, 2024
91529f8
fix: utxo ws :tada:
gomesalexandre Sep 19, 2024
745a229
chore: bump packages
gomesalexandre Sep 19, 2024
15f5eaf
feat: flag
gomesalexandre Sep 19, 2024
447787c
fix: btc things
gomesalexandre Sep 20, 2024
c9ee4ba
fix: savers consistent withdraw dust amount
gomesalexandre Sep 20, 2024
32801d8
fix: toFixed() (pun intended)
gomesalexandre Sep 20, 2024
b260f73
feat: install
gomesalexandre Sep 20, 2024
e117f2e
Merge remote-tracking branch 'origin/develop' into feat_phantom_wire_up
gomesalexandre Sep 20, 2024
972ff92
feat: cleanup
gomesalexandre Sep 21, 2024
38ec6ed
fix: accountsChanged
gomesalexandre Sep 21, 2024
e93cbe0
chore: improve copies
gomesalexandre Sep 21, 2024
0170e3d
feat: more cleanup
gomesalexandre Sep 21, 2024
557b335
feat: more cleanup
gomesalexandre Sep 21, 2024
7e1d585
narrow utxo chain support for phantom
kaladinlight Sep 23, 2024
2802da7
remove getAccountAddressesWithBalances and update to return pubkey on…
kaladinlight Sep 23, 2024
b57c24a
Merge remote-tracking branch 'origin/develop' into feat_phantom_wire_up
gomesalexandre Sep 23, 2024
87fe468
feat: add taproot TODO
gomesalexandre Sep 23, 2024
bef524b
feat: bump hdwallet to latest without polygon support
gomesalexandre Sep 23, 2024
6b4467a
update logic with new unchained response updates
kaladinlight Sep 23, 2024
b2f3a44
Merge branch 'feat_phantom_wire_up' of github.com:shapeshift/web into…
kaladinlight Sep 23, 2024
f29a203
feat: bump hdwallet
gomesalexandre Sep 23, 2024
73c99dd
fix: unbork mm
gomesalexandre Sep 23, 2024
5bb48bd
feat: mm-like providers prompt accounts connection on inactive accoun…
gomesalexandre Sep 23, 2024
bbdb455
feat: improve commentaroo
gomesalexandre Sep 23, 2024
e6ccc04
remove comment
kaladinlight Sep 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.base
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ REACT_APP_FEATURE_USDT_APPROVAL_RESET=true
REACT_APP_FEATURE_PORTALS_SWAPPER=true
REACT_APP_FEATURE_RUNEPOOL=true
REACT_APP_FEATURE_MARKETS=false
REACT_APP_FEATURE_PHANTOM_WALLET=false

# absolute URL prefix
REACT_APP_ABSOLUTE_URL_PREFIX=https://app.shapeshift.com
Expand Down
1 change: 1 addition & 0 deletions .env.dev
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,4 @@ REACT_APP_THORCHAIN_NODE_URL=https://dev-daemon.thorchain.shapeshift.com
REACT_APP_MIDGARD_URL=https://dev-indexer.thorchain.shapeshift.com/v2

REACT_APP_FEATURE_MARKETS=true
REACT_APP_FEATURE_PHANTOM_WALLET=true
1 change: 1 addition & 0 deletions .env.develop
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,4 @@ REACT_APP_THORCHAIN_NODE_URL=https://dev-daemon.thorchain.shapeshift.com
REACT_APP_MIDGARD_URL=https://dev-indexer.thorchain.shapeshift.com/v2

REACT_APP_FEATURE_MARKETS=true
REACT_APP_FEATURE_PHANTOM_WALLET=true
27 changes: 14 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,19 +92,20 @@
"@shapeshiftoss/caip": "workspace:^",
"@shapeshiftoss/chain-adapters": "workspace:^",
"@shapeshiftoss/errors": "workspace:^",
"@shapeshiftoss/hdwallet-coinbase": "1.55.5",
"@shapeshiftoss/hdwallet-core": "1.55.5",
"@shapeshiftoss/hdwallet-keepkey": "1.55.5",
"@shapeshiftoss/hdwallet-keepkey-webusb": "1.55.5",
"@shapeshiftoss/hdwallet-keplr": "1.55.5",
"@shapeshiftoss/hdwallet-ledger": "1.55.5",
"@shapeshiftoss/hdwallet-ledger-webusb": "1.55.5",
"@shapeshiftoss/hdwallet-metamask": "1.55.5",
"@shapeshiftoss/hdwallet-native": "1.55.5",
"@shapeshiftoss/hdwallet-native-vault": "1.55.5",
"@shapeshiftoss/hdwallet-shapeshift-multichain": "1.55.5",
"@shapeshiftoss/hdwallet-walletconnectv2": "1.55.5",
"@shapeshiftoss/hdwallet-xdefi": "1.55.5",
"@shapeshiftoss/hdwallet-coinbase": "1.55.6",
"@shapeshiftoss/hdwallet-core": "1.55.6",
"@shapeshiftoss/hdwallet-keepkey": "1.55.6",
"@shapeshiftoss/hdwallet-keepkey-webusb": "1.55.6",
"@shapeshiftoss/hdwallet-keplr": "1.55.6",
"@shapeshiftoss/hdwallet-ledger": "1.55.6",
"@shapeshiftoss/hdwallet-ledger-webusb": "1.55.6",
"@shapeshiftoss/hdwallet-metamask": "1.55.6",
"@shapeshiftoss/hdwallet-native": "1.55.6",
"@shapeshiftoss/hdwallet-native-vault": "1.55.6",
"@shapeshiftoss/hdwallet-phantom": "1.55.6",
"@shapeshiftoss/hdwallet-shapeshift-multichain": "1.55.6",
"@shapeshiftoss/hdwallet-walletconnectv2": "1.55.6",
"@shapeshiftoss/hdwallet-xdefi": "1.55.6",
"@shapeshiftoss/swapper": "workspace:^",
"@shapeshiftoss/types": "workspace:^",
"@shapeshiftoss/unchained-client": "workspace:^",
Expand Down
15 changes: 12 additions & 3 deletions packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
BTCOutputAddressType,
supportsBTC,
} from '@shapeshiftoss/hdwallet-core'
import { PhantomHDWallet } from '@shapeshiftoss/hdwallet-phantom'
import type { BIP44Params, UtxoChainId } from '@shapeshiftoss/types'
import { KnownChainIds, UtxoAccountType } from '@shapeshiftoss/types'
import type * as unchained from '@shapeshiftoss/unchained-client'
Expand Down Expand Up @@ -323,12 +324,15 @@ export abstract class UtxoBaseAdapter<T extends UtxoChainId> implements IChainAd

const signTxInputs: BTCSignTxInput[] = []
for (const input of inputs) {
if (!input.path) continue
// TOOD(gomes): v. dangerous, narrow this monkey patch for Phantom only
// if (!input.path) continue
kaladinlight marked this conversation as resolved.
Show resolved Hide resolved

const data = await this.providers.http.getTransaction({ txid: input.txid })

signTxInputs.push({
addressNList: bip32ToAddressNList(input.path),
// TODO(gomes): v. dangerous, should only be used for phantom since no derivation happens, and assume account 0 address 0 yadi yada
// @ts-ignore
kaladinlight marked this conversation as resolved.
Show resolved Hide resolved
addressNList: input.path ? bip32ToAddressNList(input.path) : undefined,
scriptType: accountTypeToScriptType[accountType],
amount: String(input.value),
vout: input.vout,
Expand Down Expand Up @@ -514,7 +518,12 @@ export abstract class UtxoBaseAdapter<T extends UtxoChainId> implements IChainAd
const account = await this.getAccount(
input.pubKey ?? (await this.getPublicKey(wallet, accountNumber, accountType)).xpub,
)
const addresses = (account.chainSpecific.addresses ?? []).map(address => address.pubkey)
const addresses = (() => {
// This means the account isn't an xpub account, pub a pubkey as account i.e Phantom, which doesn't expose an xpub
kaladinlight marked this conversation as resolved.
Show resolved Hide resolved
if (wallet instanceof PhantomHDWallet && !account.chainSpecific.addresses && account.pubkey)
return [account.pubkey]
return (account.chainSpecific.addresses ?? []).map(address => address.pubkey)
})()
const subscriptionId = `${toRootDerivationPath(bip44Params)}/${accountType}`

await this.providers.ws.subscribeTxs(
Expand Down
8 changes: 7 additions & 1 deletion packages/chain-adapters/src/utxo/utxoSelect/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,13 @@ export const utxoSelect = (input: UTXOSelectInput) => {
// If input contains a `from` param, the intent is to only keep the UTXOs from that address
// so we can ensure the send address is the one we want
// This doesn't do any further checks, so error-handling should be done by the caller e.g `buildSendTransaction` callsites
if (!input.from || (input.from && utxo.address === input.from)) {
if (
!input.from ||
// Accommodate for pubkey (not xpub) UTXOs which don't have an address
// Can there ever be a case where a pubkey UTXO also doesn't contain an address and this is a danger for fundus?
kaladinlight marked this conversation as resolved.
Show resolved Hide resolved
(input.from && !utxo.address) ||
(input.from && utxo.address === input.from)
) {
acc.push(sanitizedUtxo)
}
return acc
Expand Down
18 changes: 12 additions & 6 deletions src/assets/translations/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -1461,7 +1461,6 @@
"button": "Pair"
},
"failure": {
"header": "Error",
"body": "Unable to connect Keplr wallet"
}
},
Expand Down Expand Up @@ -1501,7 +1500,6 @@
"pairExistingDeviceBody": "Connecting a different Ledger device? Click 'Pair new device' to start fresh."
},
"failure": {
"header": "Error",
kaladinlight marked this conversation as resolved.
Show resolved Hide resolved
"body": "Unable to connect Ledger wallet"
},
"connectWarning": "Before connecting a chain, make sure you have the app open on your device.",
Expand All @@ -1519,7 +1517,6 @@
"button": "Pair"
},
"failure": {
"header": "Error",
"body": "Unable to connect MetaMask wallet"
},
"redirect": {
Expand All @@ -1528,6 +1525,18 @@
"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"
}
},
"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 Expand Up @@ -1583,7 +1592,6 @@
"button": "Pair"
},
"failure": {
"header": "Unable to connect Coinbase wallet",
"body": "Unable to connect Coinbase wallet"
}
},
Expand All @@ -1598,7 +1606,6 @@
"button": "Pair"
},
"failure": {
"header": "Error",
"body": "Unable to connect WalletConnect wallet"
}
},
Expand All @@ -1614,7 +1621,6 @@
"button": "Pair"
},
"failure": {
"header": "Error",
"body": "Unable to connect XDEFI wallet"
},
"redirect": {
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/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ const validators = {
REACT_APP_FEATURE_MARKETS: bool({ default: false }),
REACT_APP_PORTALS_BASE_URL: url(),
REACT_APP_ZERION_BASE_URL: url(),
REACT_APP_FEATURE_PHANTOM_WALLET: bool({ default: false }),
}

function reporter<T>({ errors }: envalid.ReporterOptions<T>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FailureModal } from 'context/WalletProvider/components/FailureModal'
export const CoinbaseFailure = () => {
return (
<FailureModal
headerText={'walletProvider.coinbase.failure.header'}
headerText={'common.error'}
bodyText={'walletProvider.coinbase.failure.body'}
></FailureModal>
)
Expand Down
2 changes: 1 addition & 1 deletion src/context/WalletProvider/Keplr/components/Failure.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FailureModal } from 'context/WalletProvider/components/FailureModal'
export const KeplrFailure = () => {
return (
<FailureModal
headerText={'walletProvider.keplr.failure.header'}
headerText={'common.error'}
bodyText={'walletProvider.keplr.failure.body'}
></FailureModal>
)
Expand Down
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
2 changes: 1 addition & 1 deletion src/context/WalletProvider/Ledger/components/Failure.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FailureModal } from 'context/WalletProvider/components/FailureModal'
export const LedgerFailure = () => {
return (
<FailureModal
headerText={'walletProvider.ledger.failure.header'}
headerText={'common.error'}
bodyText={'walletProvider.ledger.failure.body'}
></FailureModal>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ import { FailureModal } from 'context/WalletProvider/components/FailureModal'

export const MetaMaskFailure = () => {
return (
<FailureModal
headerText={'walletProvider.metaMask.failure.header'}
bodyText={'walletProvider.metaMask.failure.body'}
/>
<FailureModal headerText={'common.error'} bodyText={'walletProvider.metaMask.failure.body'} />
)
}
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()
kaladinlight marked this conversation as resolved.
Show resolved Hide resolved

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={'common.error'}
bodyText={'walletProvider.phantom.errors.connectFailure'}
/>
)
}
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',
}
3 changes: 3 additions & 0 deletions src/context/WalletProvider/SelectModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ const WalletSelectItem = ({
const isCoinbaseEnabled = getConfig().REACT_APP_FEATURE_COINBASE_WALLET
if (walletType === KeyManager.Coinbase && !isCoinbaseEnabled) return null

const isPhantomEnabled = getConfig().REACT_APP_FEATURE_PHANTOM_WALLET
if (walletType === KeyManager.Phantom && !isPhantomEnabled) return null

const isWalletConnectV2Enabled = getConfig().REACT_APP_FEATURE_WALLET_CONNECT_V2
if (walletType === KeyManager.WalletConnectV2 && !isWalletConnectV2Enabled) return null

Expand Down
Loading
Loading