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(wallets): trezor wallet support #3020

Merged
merged 34 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0c835d5
chore: trezor setup
shoom3301 Aug 7, 2023
7786555
feat(trezor): sign and send transaction
shoom3301 Aug 8, 2023
e1d590c
feat(trezor): display approve tx error in modal
shoom3301 Aug 8, 2023
36072a3
fix(trezor): use gas price for tx
shoom3301 Aug 8, 2023
33e844d
feat(trezor): sign typed data
shoom3301 Aug 8, 2023
9d660bd
fix(trezor): display icon in pending tx modal
shoom3301 Aug 8, 2023
d231b4b
chore: fix trezor icon
shoom3301 Aug 8, 2023
a676447
fix(trezor): allow switching network
shoom3301 Aug 8, 2023
abe31ad
feat(trezor): selector for account index
shoom3301 Aug 8, 2023
8d0dc00
refactor(trezor): extract sendTransactionHandler
shoom3301 Aug 8, 2023
2cbd907
refactor(trezor): extract TrezorConnector
shoom3301 Aug 8, 2023
9b1148e
Merge branch 'develop' of https://github.com/cowprotocol/cowswap into…
shoom3301 Aug 8, 2023
febf67a
chore: fix double activation
shoom3301 Aug 8, 2023
ad89d37
fix: fix metamask approving banner conditions
shoom3301 Aug 8, 2023
7611f1a
chore: fix gasPrice
shoom3301 Aug 8, 2023
2cdad5b
feat(wallets): display connection error in modal
shoom3301 Aug 9, 2023
d38f2dd
fix: dispose trezor on connection fail
shoom3301 Aug 9, 2023
15a331e
fix: deactivate connector on trezor disconnect
shoom3301 Aug 9, 2023
227271b
Merge branch 'develop' of https://github.com/cowprotocol/cowswap into…
shoom3301 Aug 11, 2023
d38b3bf
chore: merge develop
shoom3301 Aug 11, 2023
3f01fe3
chore: fix
shoom3301 Aug 11, 2023
0e7dcab
chore: temp fix
shoom3301 Aug 11, 2023
006226c
chore: temp fix
shoom3301 Aug 11, 2023
dddc42a
refactor: address code review issues
shoom3301 Aug 11, 2023
60d997c
chore: revert fix
shoom3301 Aug 11, 2023
d3d866e
fix: clean state on deactivate
shoom3301 Aug 11, 2023
b561239
chore: refactor
shoom3301 Aug 11, 2023
3a3ceb6
feat(trezor): load first 100 accounts
shoom3301 Aug 14, 2023
35610f1
feat(trezor): select account index in one click
shoom3301 Aug 14, 2023
4e64011
feat(trezor): hide the wallet under feature-flag
shoom3301 Aug 14, 2023
4fbe323
chore: hide Trezor by feature-flag
shoom3301 Aug 14, 2023
8906b31
feat(trezor): display accounts balances
shoom3301 Aug 14, 2023
98a6b65
Merge branch 'develop' of https://github.com/cowprotocol/cowswap into…
shoom3301 Aug 14, 2023
ff3ff07
chore: cosmos AccountIndexSelect
shoom3301 Aug 14, 2023
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 craco.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ module.exports = {
http: require.resolve('stream-http'),
https: require.resolve('https-browserify'),
crypto: require.resolve('crypto-browserify'),
zlib: false,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@trezor/connect-plugin-ethereum requires @metamask/eth-sig-util that requires zlib

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding @metamask/eth-sig-util

Does it need to be explicitly installed? Isn't it enough to have it installed by the @trezor package?

},
},
}),
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
"@lingui/core": "^3.15.0",
"@lingui/macro": "^3.15.0",
"@lingui/react": "^3.15.0",
"@metamask/eth-sig-util": "^5.0.2",
"@metamask/jazzicon": "^2.0.0",
"@popperjs/core": "^2.4.4",
"@reach/dialog": "^0.18.0",
Expand All @@ -96,6 +97,8 @@
"@sentry/react": "^7.3.0",
"@sentry/tracing": "^7.3.0",
"@sentry/webpack-plugin": "^1.17.1",
"@trezor/connect-plugin-ethereum": "^9.0.1",
"@trezor/connect-web": "^9.0.11",
"@uniswap/governance": "^1.0.2",
"@uniswap/liquidity-staker": "^1.0.2",
"@uniswap/merkle-distributor": "1.0.1",
Expand Down
22 changes: 17 additions & 5 deletions src/common/containers/TradeApprove/TradeApproveButton.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React, { useCallback } from 'react'
import React, { useCallback, useState } from 'react'

import { Currency, CurrencyAmount } from '@uniswap/sdk-core'

import { useShouldZeroApprove } from 'common/hooks/useShouldZeroApprove'
import { useZeroApprove } from 'common/hooks/useZeroApprove'
import { ApproveButton } from 'common/pure/ApproveButton'
import { CowModal } from 'common/pure/Modal'
import { TransactionErrorContent } from 'common/pure/TransactionErrorContent'

import { useTradeApproveCallback } from './useTradeApproveCallback'
import { useTradeApproveState } from './useTradeApproveState'
Expand All @@ -24,16 +26,26 @@ export function TradeApproveButton(props: TradeApproveButtonProps) {
const tradeApproveCallback = useTradeApproveCallback(amountToApprove)
const shouldZeroApprove = useShouldZeroApprove(amountToApprove)
const zeroApprove = useZeroApprove(amountToApprove.currency)
const [error, setError] = useState<string | null>(null)
const onDismissError = () => setError(null)

const handleApprove = useCallback(async () => {
if (shouldZeroApprove) {
await zeroApprove()
try {
if (shouldZeroApprove) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before we didn't display errors from token approving

await zeroApprove()
}
await tradeApproveCallback()
} catch (error) {
setError(typeof error === 'string' ? error : error.message || error.toString())
}
await tradeApproveCallback()
}, [tradeApproveCallback, zeroApprove, shouldZeroApprove])
}, [tradeApproveCallback, zeroApprove, shouldZeroApprove, setError])

return (
<>
<CowModal isOpen={!!error} onDismiss={onDismissError}>
{error && <TransactionErrorContent message={error} onDismiss={onDismissError} />}
</CowModal>

<ApproveButton isDisabled={isDisabled} currency={currency} onClick={handleApprove} state={approvalState} />

{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { getStatusIcon } from 'modules/account/containers/AccountDetails'
import { useGnosisSafeInfo, useWalletDetails, useWalletInfo } from 'modules/wallet'
import { getIsMetaMask } from 'modules/wallet/api/utils/connection'
import { getWeb3ReactConnection } from 'modules/wallet/web3-react/connection'
import { walletConnectConnection } from 'modules/wallet/web3-react/connection/walletConnect'
import { injectedConnection } from 'modules/wallet/web3-react/connection/injected'

import {
ApproveComparison,
Expand Down Expand Up @@ -144,7 +144,7 @@ export function LegacyConfirmationPendingContent({
operationType === ConfirmOperationType.APPROVE_TOKEN &&
isMetaMask &&
isNotMobile &&
connectionType !== walletConnectConnection
connectionType === injectedConnection

return (
<Wrapper>
Expand Down
13 changes: 12 additions & 1 deletion src/legacy/state/gas/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useSetAtom } from 'jotai'
import { useCallback } from 'react'

import { SupportedChainId as ChainId } from '@cowprotocol/cow-sdk'
Expand All @@ -7,6 +8,8 @@ import { useSelector, useDispatch } from 'react-redux'
import { AppDispatch } from 'legacy/state'
import { AppState } from 'legacy/state'

import { gasPriceAtom } from 'modules/gasPirce'

import { updateGasPrices, UpdateGasPrices } from './actions'
import { GasState } from './reducer'

Expand All @@ -18,5 +21,13 @@ export function useGasPrices(chainId?: ChainId) {

export function useUpdateGasPrices() {
const dispatch = useDispatch<AppDispatch>()
return useCallback((gasParams: UpdateGasPrices) => dispatch(updateGasPrices(gasParams)), [dispatch])
const setGasPrice = useSetAtom(gasPriceAtom)

return useCallback(
(gasParams: UpdateGasPrices) => {
dispatch(updateGasPrices(gasParams))
setGasPrice(gasParams)
},
[dispatch, setGasPrice]
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useAtom } from 'jotai/index'
import { FormEvent, useCallback, useRef } from 'react'

import styled from 'styled-components/macro'

import { hwAccountIndexAtom } from 'modules/wallet/api/state'

const Wrapper = styled.form`
font-size: 14px;
margin: 10px 0;
`

// TODO: add styles
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

export function HwAccountIndexSelector() {
const accountIndexRef = useRef<HTMLInputElement>(null)
const [hwAccountIndex, setHwAccountIndex] = useAtom(hwAccountIndexAtom)

const onHwAccountIndexChange = useCallback(
(event: FormEvent) => {
event.preventDefault()

setHwAccountIndex(+(accountIndexRef.current?.value || 0))
},
[setHwAccountIndex]
)

return (
<Wrapper onSubmit={(event) => onHwAccountIndexChange(event)}>
<p>Hardware account index:</p>
<input ref={accountIndexRef} placeholder={hwAccountIndex.toString()} type="number" step={1} />
<button>Update</button>
</Wrapper>
)
}
108 changes: 40 additions & 68 deletions src/modules/account/containers/AccountDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,20 @@ import FortmaticIcon from 'modules/wallet/api/assets/formatic.png'
import KeystoneImage from 'modules/wallet/api/assets/keystone.svg'
import LedgerIcon from 'modules/wallet/api/assets/ledger.svg'
import TallyIcon from 'modules/wallet/api/assets/tally.svg'
import TrezorIcon from 'modules/wallet/api/assets/trezor.svg'
import TrustIcon from 'modules/wallet/api/assets/trust.svg'
import WalletConnectIcon from 'modules/wallet/api/assets/walletConnectIcon.svg'
import { Identicon } from 'modules/wallet/api/container/Identicon'
import { useWalletDetails } from 'modules/wallet/api/hooks'
import {
getConnectionName,
getIsCoinbaseWallet,
getIsMetaMask,
getIsTrustWallet,
} from 'modules/wallet/api/utils/connection'
import { getConnectionName, getIsCoinbaseWallet, getIsMetaMask } from 'modules/wallet/api/utils/connection'
import { getWeb3ReactConnection } from 'modules/wallet/web3-react/connection'
import { coinbaseWalletConnection } from 'modules/wallet/web3-react/connection/coinbase'
import { fortmaticConnection } from 'modules/wallet/web3-react/connection/formatic'
import { injectedConnection } from 'modules/wallet/web3-react/connection/injected'
import { keystoneConnection } from 'modules/wallet/web3-react/connection/keystone'
import { ledgerConnection } from 'modules/wallet/web3-react/connection/ledger'
import { tallyWalletConnection } from 'modules/wallet/web3-react/connection/tally'
import { trustWalletConnection } from 'modules/wallet/web3-react/connection/trust'
import { walletConnectConnection } from 'modules/wallet/web3-react/connection/walletConnect'
import { walletConnectConnectionV2 } from 'modules/wallet/web3-react/connection/walletConnectV2'

import { UNSUPPORTED_WALLET_TEXT } from 'common/containers/WalletUnsupportedNetworkBanner'
import { useIsProviderNetworkUnsupported } from 'common/hooks/useIsProviderNetworkUnsupported'

import { HwAccountIndexSelector } from './HwAccountIndexSelector'
import {
AccountControl,
AccountGroupingRow,
Expand Down Expand Up @@ -91,11 +81,29 @@ export function renderActivities(activities: ActivityDescriptors[]) {
)
}

export function getStatusIcon(connector?: Connector | ConnectionType, walletDetails?: WalletDetails, size?: number) {
if (!connector) {
return null
}
const IDENTICON_KEY = 'Identicon'

const walletIcons: Record<ConnectionType, 'Identicon' | string> = {
[ConnectionType.INJECTED]: IDENTICON_KEY,
[ConnectionType.INJECTED_WIDGET]: IDENTICON_KEY,
[ConnectionType.GNOSIS_SAFE]: IDENTICON_KEY,
[ConnectionType.NETWORK]: IDENTICON_KEY,
[ConnectionType.ZENGO]: IDENTICON_KEY,
[ConnectionType.AMBIRE]: IDENTICON_KEY,
[ConnectionType.ALPHA]: IDENTICON_KEY,
[ConnectionType.COINBASE_WALLET]: CoinbaseWalletIcon,
[ConnectionType.FORTMATIC]: FortmaticIcon,
[ConnectionType.TALLY]: TallyIcon,
[ConnectionType.TRUST]: TrustIcon,
[ConnectionType.TALLY]: TallyIcon,
[ConnectionType.LEDGER]: LedgerIcon,
[ConnectionType.TREZOR]: TrezorIcon,
[ConnectionType.KEYSTONE]: KeystoneImage,
[ConnectionType.WALLET_CONNECT]: WalletConnectIcon,
[ConnectionType.WALLET_CONNECT_V2]: WalletConnectIcon,
}

export function getStatusIcon(connector: Connector, walletDetails?: WalletDetails, size?: number) {
const connectionType = getWeb3ReactConnection(connector)

if (walletDetails && !walletDetails.isSupportedWallet) {
Expand All @@ -108,64 +116,26 @@ export function getStatusIcon(connector?: Connector | ConnectionType, walletDeta
</MouseoverTooltip>
)
/* eslint-enable jsx-a11y/accessible-emoji */
} else if (walletDetails?.icon) {
}
if (walletDetails?.icon) {
return (
<IconWrapper size={16}>
<img src={walletDetails.icon} alt={`${walletDetails?.walletName || 'wallet'} logo`} />
</IconWrapper>
)
} else if (connectionType === injectedConnection) {
}

const icon = walletIcons[connectionType.type]

if (icon === IDENTICON_KEY) {
return <Identicon size={size} />
} else if (connectionType === coinbaseWalletConnection) {
return (
<IconWrapper size={16}>
<img src={CoinbaseWalletIcon} alt={'Coinbase wallet logo'} />
</IconWrapper>
)
} else if (connectionType === fortmaticConnection) {
return (
<IconWrapper size={16}>
<img src={FortmaticIcon} alt={'Fortmatic logo'} />
</IconWrapper>
)
} else if (connectionType === tallyWalletConnection) {
return (
<IconWrapper size={16}>
<img src={TallyIcon} alt={'Tally logo'} />
</IconWrapper>
)
} else if (connectionType === trustWalletConnection || getIsTrustWallet(null, walletDetails?.walletName)) {
return (
<IconWrapper size={16}>
<img src={TrustIcon} alt={'Trust logo'} />
</IconWrapper>
)
} else if (connectionType === tallyWalletConnection) {
return (
<IconWrapper size={16}>
<img src={TallyIcon} alt={'tally logo'} />
</IconWrapper>
)
} else if (connectionType === ledgerConnection) {
return (
<IconWrapper size={16}>
<img src={LedgerIcon} alt={'Ledger logo'} />
</IconWrapper>
)
} else if (connectionType === keystoneConnection) {
return (
<IconWrapper size={16}>
<img src={KeystoneImage} alt={'Keystone logo'} />
</IconWrapper>
)
} else if (connectionType === walletConnectConnection) {
return (
<IconWrapper size={16}>
<img src={WalletConnectIcon} alt={'Wallet connect logo'} />
</IconWrapper>
)
}
return null

return (
<IconWrapper size={16}>
<img src={icon} alt={`${connectionType.type} logo`} />
</IconWrapper>
)
}

export interface AccountDetailsProps {
Expand Down Expand Up @@ -240,6 +210,8 @@ export function AccountDetails({
)}
</WalletWrapper>

<HwAccountIndexSelector />

<WalletActions>
{' '}
{networkLabel && !isChainIdUnsupported && (
Expand Down
3 changes: 2 additions & 1 deletion src/modules/application/containers/App/Updaters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { UploadToIpfsUpdater } from 'modules/appData/updater/UploadToIpfsUpdater
import { InjectedWidgetUpdater } from 'modules/injectedWidget'
import { EthFlowDeadlineUpdater, EthFlowSlippageUpdater } from 'modules/swap/state/EthFlow/updaters'
import { TokensListUpdater } from 'modules/tokensList/updaters/TokensListUpdater'
import { WalletUpdater } from 'modules/wallet'
import { WalletUpdater, HwAccountIndexUpdater } from 'modules/wallet'

import { TotalSurplusUpdater } from 'common/state/totalSurplusState'
import { ThemeFromUrlUpdater } from 'common/updaters/ThemeFromUrlUpdater'
Expand All @@ -30,6 +30,7 @@ export function Updaters() {
return (
<>
<WalletUpdater />
<HwAccountIndexUpdater />
<TokensListUpdater />
<ListsUpdater />
<UserUpdater />
Expand Down
1 change: 1 addition & 0 deletions src/modules/gasPirce/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './state/gasPriceAtom'
10 changes: 10 additions & 0 deletions src/modules/gasPirce/state/gasPriceAtom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { atom } from 'jotai'

interface GasPriceState {
lastUpdate: string
average: string | null
fast: string | null
slow: string | null
}

export const gasPriceAtom = atom<GasPriceState | null>(null)
11 changes: 11 additions & 0 deletions src/modules/wallet/api/assets/trezor.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/modules/wallet/api/state.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { atom } from 'jotai'
import { atomWithStorage } from 'jotai/utils'

import { shortenAddress } from 'legacy/utils'

Expand Down Expand Up @@ -30,3 +31,5 @@ export const walletDisplayedAddress = atom((get) => {

return ensName || (account ? shortenAddress(account) : '')
})

export const hwAccountIndexAtom = atomWithStorage<number>('hw-account-index:v1', 0)
shoom3301 marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions src/modules/wallet/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export enum ConnectionType {
TRUST = 'TRUST',
LEDGER = 'LEDGER',
KEYSTONE = 'KEYSTONE',
TREZOR = 'TREZOR',
}

export const BACKFILLABLE_WALLETS = [ConnectionType.INJECTED, ConnectionType.WALLET_CONNECT]
Expand Down
Loading
Loading