Skip to content

Commit

Permalink
feat(widget): connect wallet (#3262)
Browse files Browse the repository at this point in the history
  • Loading branch information
shoom3301 authored Oct 25, 2023
1 parent aded5c5 commit d5ba5c7
Show file tree
Hide file tree
Showing 10 changed files with 643 additions and 53 deletions.
20 changes: 0 additions & 20 deletions apps/widget-configurator/src/app/app.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import Select, { SelectChangeEvent } from '@mui/material/Select'
export function TradeModesControl({ state }: { state: [TradeType[], Dispatch<SetStateAction<TradeType[]>>] }) {
const [tradeModes, setTradeModes] = state
const handleTradeModeChange = (event: SelectChangeEvent<TradeType[]>) => {
if (!event.target.value.length) return

setTradeModes(event.target.value as TradeType[])
}

Expand Down
49 changes: 49 additions & 0 deletions apps/widget-configurator/src/app/configurator/hooks/useProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useEffect, useState } from 'react'

import { EthereumProvider } from '@cowprotocol/widget-lib'

import { useAccount, useConfig } from 'wagmi'

import EventEmitter from 'events'

const onDisconnect = new EventEmitter()

export function useProvider(): EthereumProvider | null {
const config = useConfig()
const { isDisconnected } = useAccount()
const [provider, setProvider] = useState<EthereumProvider | null>(null)

useEffect(() => {
return config.subscribe(({ connector }) => {
connector?.getProvider().then((provider) => {
setProvider(getEthereumProvider(provider, onDisconnect))
})
})
}, [config])

useEffect(() => {
if (!provider || !isDisconnected) return

onDisconnect.emit('disconnect')
}, [provider, isDisconnected])

return provider
}

function getEthereumProvider(provider: EthereumProvider, onDisconnect: EventEmitter): EthereumProvider {
return {
request(...args) {
return provider.request(...args)
},
enable(...args) {
return provider.enable(...args)
},
on(event: string, args: unknown) {
if (event === 'disconnect' || event === 'close') {
return onDisconnect.on('disconnect', args)
} else {
return provider.on(event, args)
}
},
}
}
20 changes: 18 additions & 2 deletions apps/widget-configurator/src/app/configurator/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
cowSwapWidget,
CowSwapWidgetParams,
CowSwapWidgetSettings,
EthereumProvider,
TradeType,
UpdateWidgetCallback,
} from '@cowprotocol/widget-lib'
Expand All @@ -24,6 +25,7 @@ import { CurrentTradeTypeControl } from './controls/CurrentTradeTypeControl'
import { NetworkControl, NetworkOption, NetworkOptions } from './controls/NetworkControl'
import { ThemeControl } from './controls/ThemeControl'
import { TradeModesControl } from './controls/TradeModesControl'
import { useProvider } from './hooks/useProvider'
import { DrawerStyled, WrapperStyled, ContentStyled } from './styled'

import { ColorModeContext } from '../../theme/ColorModeContext'
Expand Down Expand Up @@ -62,7 +64,10 @@ export function Configurator({ title }: { title: string }) {
const iframeContainerRef = useRef<HTMLDivElement>(null)
const updateWidgetRef = useRef<UpdateWidgetCallback | null>(null)

const [isDynamicHeightEnabled, setDynamicHeightEnabled] = useState(false)
const [isDynamicHeightEnabled, setDynamicHeightEnabled] = useState(true)

const provider = useProvider()
const providerRef = useRef<EthereumProvider | null>()

useEffect(() => {
const widgetContainer = iframeContainerRef.current
Expand All @@ -74,6 +79,7 @@ export function Configurator({ title }: { title: string }) {
metaData: { appKey: 'YOUR_APP_ID', url: 'https://YOUR_APP_URL' },
width: 400,
height: 640,
provider: provider,
}

const settings: CowSwapWidgetSettings = {
Expand All @@ -93,12 +99,18 @@ export function Configurator({ title }: { title: string }) {
},
}

// Re-initialize widget when provider is changed
if (provider && providerRef.current !== provider) {
updateWidgetRef.current = null
}

if (updateWidgetRef.current) {
updateWidgetRef.current(settings)
} else {
updateWidgetRef.current = cowSwapWidget(params, settings)
}
}, [
provider,
chainId,
enabledTradeTypes,
sellToken,
Expand All @@ -110,6 +122,10 @@ export function Configurator({ title }: { title: string }) {
isDynamicHeightEnabled,
])

useEffect(() => {
providerRef.current = provider
}, [provider])

const handleWidgetRefreshClick = () => {
setMode('light')
setSellToken(DEFAULT_STATE.sellToken)
Expand All @@ -135,7 +151,7 @@ export function Configurator({ title }: { title: string }) {
variant="contained"
onClick={handleWidgetRefreshClick}
>
Connect
<w3m-button />
</LoadingButton>

<Divider variant="middle">General</Divider>
Expand Down
32 changes: 24 additions & 8 deletions apps/widget-configurator/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import { StrictMode, useMemo } from 'react'

import { CssBaseline, GlobalStyles } from '@mui/material'
import Box from '@mui/material/Box'
import { createTheme, PaletteOptions, ThemeProvider } from '@mui/material/styles'
import { createRoot } from 'react-dom/client'
import { WagmiConfig } from 'wagmi'

import App from './app/app'
import { Configurator } from './app/configurator'
import { ColorModeContext, globalStyles } from './theme/ColorModeContext'
import { commonTypography } from './theme/commonTypography'
import { useColorMode } from './theme/hooks/useColorMode'
import { darkPalette, lightPalette } from './theme/paletteOptions'
import { wagmiConfig } from './wagmiConfig'

const WrapperStyled = {
display: 'flex',
flexDirection: 'column',
height: '100%',
width: '100%',
}

function Root() {
const colorMode = useColorMode()
Expand All @@ -24,13 +34,19 @@ function Root() {
}, [mode])

return (
<ColorModeContext.Provider value={colorMode}>
<ThemeProvider theme={theme}>
<CssBaseline />
<GlobalStyles styles={globalStyles(theme, colorMode.mode)} />
<App />
</ThemeProvider>
</ColorModeContext.Provider>
<StrictMode>
<WagmiConfig config={wagmiConfig}>
<ColorModeContext.Provider value={colorMode}>
<ThemeProvider theme={theme}>
<CssBaseline />
<GlobalStyles styles={globalStyles(theme, colorMode.mode)} />
<Box sx={WrapperStyled}>
<Configurator title="CoW Widget" />
</Box>
</ThemeProvider>
</ColorModeContext.Provider>
</WagmiConfig>
</StrictMode>
)
}

Expand Down
20 changes: 20 additions & 0 deletions apps/widget-configurator/src/wagmiConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createWeb3Modal, defaultWagmiConfig } from '@web3modal/wagmi/react'
import { gnosis, goerli, mainnet } from 'wagmi/chains'

const WC_PROJECT_ID = process.env.REACT_APP_WC_PROJECT_ID
const WC_DEFAULT_PROJECT_ID = 'a6cc11517a10f6f12953fd67b1eb67e7'

const projectId = WC_PROJECT_ID || WC_DEFAULT_PROJECT_ID

const metadata = {
name: 'CoW Widget Configurator',
description: 'Injectable UI of CoWSwap',
url: 'https://swap.cow.fi',
icons: ['https://swap.cow.fi/favicon.png'],
}

const chains = [mainnet, gnosis, goerli]

export const wagmiConfig = defaultWagmiConfig({ chains, projectId, metadata })

createWeb3Modal({ wagmiConfig, projectId, chains })
16 changes: 12 additions & 4 deletions libs/wallet/src/web3-react/connectors/Injected/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ interface injectedWalletConstructorArgs {
searchKeywords: string[]
}

function parseChainId(chainId: string) {
function parseChainId(chainId: string | number): number {
if (typeof chainId === 'number') return chainId

if (!chainId.startsWith('0x')) {
return Number(chainId)
}
Expand All @@ -50,7 +52,10 @@ export class InjectedWallet extends Connector {
// Based on https://github.com/Uniswap/web3-react/blob/de97c00c378b7909dfbd8a06558ed12e1f796caa/packages/metamask/src/index.ts#L130 with some changes
async activate(desiredChainIdOrChainParameters?: number | AddEthereumChainParameter): Promise<void> {
let cancelActivation: () => void
if (!this.provider?.isConnected?.()) cancelActivation = this.actions.startActivation()

if (!this.provider?.isConnected?.()) {
cancelActivation = this.actions.startActivation()
}

return this.isomorphicInitialize()
.then(async () => {
Expand Down Expand Up @@ -145,13 +150,16 @@ export class InjectedWallet extends Connector {
this.actions.update({ chainId: parseChainId(chainId) })
})

provider.on('disconnect', (error: ProviderRpcError): void => {
const onDisconnect = (error: ProviderRpcError): void => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.provider?.request({ method: 'PUBLIC_disconnectSite' })

this.actions.resetState()
this.onError?.(error)
})
}

provider.on('disconnect', onDisconnect)
provider.on('close', onDisconnect)

provider.on('chainChanged', (chainId: string): void => {
this.actions.update({ chainId: parseChainId(chainId) })
Expand Down
2 changes: 1 addition & 1 deletion libs/widget-lib/src/JsonRpcManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { EthereumProvider, JsonRpcRequest } from './types'

const JSON_PRC_V = '2.0'
const TARGET_ORIGIN = '*'
const EVENTS = ['connect', 'disconnect', 'chainChanged', 'accountsChanged']
const EVENTS = ['connect', 'disconnect', 'close', 'chainChanged', 'accountsChanged']

export class JsonRpcManager {
ethereumProvider: EthereumProvider | null = null
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
"@web3-react/network": "^8.2.3",
"@web3-react/url": "^8.2.3",
"@web3-react/walletconnect-v2": "^8.5.1",
"@web3modal/wagmi": "^3.2.0-alpha.1",
"bnc-sdk": "^4.6.0",
"buffer": "^6.0.3",
"cids": "^1.0.0",
Expand Down Expand Up @@ -175,6 +176,8 @@
"use-async-memo": "^1.2.4",
"use-count-up": "^2.2.5",
"use-resize-observer": "^8.0.0",
"viem": "1.16.6",
"wagmi": "^1.4.5",
"wcag-contrast": "^3.0.0",
"web-vitals": "^2.1.4"
},
Expand Down
Loading

0 comments on commit d5ba5c7

Please sign in to comment.