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

Support Wallet Connect #243

Merged
merged 27 commits into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d75787b
Basic set up for Wallet Connect
taoalpha Jan 15, 2022
c394161
Move connect to integrations folder
taoalpha Jan 16, 2022
136e871
Support QR scanner for wallet connect
taoalpha Jan 22, 2022
44223f7
Fix the style of the prompt screen
taoalpha Jan 22, 2022
e2e39e8
Show list of pending requests
taoalpha Jan 23, 2022
7577179
make supported files human readable; fix onScan silent failures due t…
polymorpher Mar 9, 2022
37d3a9b
Revert qr change and add some new entries and fixes
taoalpha Mar 29, 2022
9f0d245
Merge branch 'master' into taoalpha/walletconnect
polymorpher Apr 9, 2022
3ff7c7d
Merge branch 'master' into taoalpha/walletconnect
polymorpher Apr 20, 2022
3afad8b
Merge branch 'master' into taoalpha/walletconnect
polymorpher Dec 27, 2023
7a28132
fix outdated packages; WC wip
polymorpher Dec 28, 2023
fb92303
revert to antd 4.17.0 to fix bugs
polymorpher Dec 28, 2023
3f82058
WalletConnect v2
polymorpher Dec 29, 2023
55b5023
fix wc bugs
polymorpher Dec 30, 2023
e5a096c
fix wallectconnect ui and bugs
polymorpher Dec 30, 2023
1483f6c
add eip712 typed data support; implement web3provider for walletconne…
polymorpher Dec 31, 2023
8e803ff
cleanup dep
polymorpher Dec 31, 2023
fa04857
ditto
polymorpher Dec 31, 2023
400fd6c
support using raw message hash for signing
polymorpher Dec 31, 2023
27ffd8a
allow WC eth_sign only when dev mode is activated
polymorpher Dec 31, 2023
9898035
WalletConnectActionModal
polymorpher Dec 31, 2023
43a1ba4
Wallet Connect tool in ui
polymorpher Jan 1, 2024
cc8808f
remove client side web3 dep (duplicating root module)
polymorpher Jan 1, 2024
e8f7994
proper web3 resolution; fix bigint type issue
polymorpher Jan 2, 2024
d693f89
fix bug and end-to-end test
polymorpher Jan 2, 2024
7af66b7
show equivalent hex address in address input; show wallet name in wC
polymorpher Jan 2, 2024
7506ab7
remvoe console.log
polymorpher Jan 2, 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 code/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
"@sentry/react": "^6.8.0",
"@sentry/tracing": "^6.8.0",
"@transak/transak-sdk": "^1.0.31",
"@walletconnect/client": "^1.7.1",
"antd": "^4.16.2",
"axios": "^0.21.1",
"bn.js": "^5.2.0",
Expand Down
1 change: 1 addition & 0 deletions code/client/src/app.less
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
@import '~antd/es/image/style/index.less';
@import '~antd/es/input/style/index.less';
@import '~antd/es/layout/style/index.less';
@import '~antd/es/list/style/index.less';
@import '~antd/es/menu/style/index.less';
@import '~antd/es/modal/style/index.less';
@import '~antd/es/notification/style/index.less';
Expand Down
4 changes: 2 additions & 2 deletions code/client/src/components/QrCodeScanner.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import UploadOutlined from '@ant-design/icons/UploadOutlined'
import jsQR from 'jsqr'
import { getDataURLFromFile, getTextFromFile } from './Common'

const QrCodeScanner = ({ onScan, shouldInit, style }) => {
const QrCodeScanner = ({ onScan, shouldInit, style, uploadBtnText = 'Use Image or JSON Instead' }) => {
const ref = useRef()
const { isMobile } = useWindowDimensions()
const [videoDevices, setVideoDevices] = useState([])
Expand Down Expand Up @@ -160,7 +160,7 @@ const QrCodeScanner = ({ onScan, shouldInit, style }) => {
beforeUpload={beforeUpload}
onChange={onQrcodeChange}
>
<Button shape='round' icon={qrCodeImageUploading ? <LoadingOutlined /> : <UploadOutlined />}>Use Image or JSON Instead</Button>
<Button shape='round' icon={qrCodeImageUploading ? <LoadingOutlined /> : <UploadOutlined />}>{uploadBtnText}</Button>
</Upload>
</Row>
</>
Expand Down
1 change: 0 additions & 1 deletion code/client/src/integration/Common.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export const WalletSelector = ({ from, onAddressSelected, filter = e => e, disab
const network = useSelector(state => state.global.network)
const wallets = useSelector(state => state.wallet)
const walletList = Object.keys(wallets).map(e => wallets[e]).filter(e => e.network === network && (showOlderVersions ? (!e.temp || !util.isEmptyAddress(e.forwardAddress)) : !e.temp))
console.log(wallets, walletList)
from = util.safeNormalizedAddress(from)
const selectedWallet = from && wallets[from]
// console.log('selectedWallet', from, selectedWallet)
Expand Down
8 changes: 6 additions & 2 deletions code/client/src/integration/WalletAuth.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import AnimatedSection from '../components/AnimatedSection'
import RequestSignature from './RequestSignature'
import message from '../message'
import { Text } from '../components/Text'
import WalletConnect from './WalletConnect'

const WalletAuth = () => {
const dispatch = useDispatch()
Expand All @@ -21,8 +22,7 @@ const WalletAuth = () => {

const qs = querystring.parse(location.search)
const callback = qs.callback && Buffer.from(qs.callback, 'base64').toString()
const caller = qs.caller
const network = qs.network
const { wc, caller, network } = qs
const { amount, dest, from, calldata } = qs
const { message: msg, raw, duration, comment } = qs
// if (!action || !callback || !caller) {
Expand All @@ -40,6 +40,10 @@ const WalletAuth = () => {
}
}, [network])

if (action === 'walletconnect') {
return <WalletConnect wc={wc} />
}

if (!action || !callback || !caller) {
message.error('The app did not specify a callback, an action, or its identity. Please ask the app developer to fix it.')
return (
Expand Down
271 changes: 271 additions & 0 deletions code/client/src/integration/WalletConnect.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
import React, { useState, useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import WalletConnectClient from '@walletconnect/client'
import { InputBox, Link, Text } from '../components/Text'
import Image from 'antd/es/image'
import { Row } from 'antd/es/grid'
import ConfigProvider from 'antd/es/config-provider'
import Button from 'antd/es/button'
import cacheActions from '../state/modules/cache/actions'
import AnimatedSection from '../components/AnimatedSection'
import message from '../message'
import Spin from 'antd/es/spin'
import util, { checkCamera } from '../util'
import QrCodeScanner from '../components/QrCodeScanner'
import { WalletSelector } from './Common'
import Send from '../pages/Show/Send'
import List from 'antd/es/list'

const PromptView = ({ peerMeta, approveSession, rejectSession }) => {
return (
<>
<Row type='flex' justify='center' align='middle'>
<Image style={{ maxWidth: '100px' }} src={peerMeta.icons[0]} alt={peerMeta.name} />
</Row>
<Row type='flex' justify='center' align='middle'>
<Text>{peerMeta.name}</Text>
</Row>
<Row type='flex' justify='center' align='middle'>
<Text>{peerMeta.description}</Text>
</Row>
<Row type='flex' justify='center' align='middle'>
<Link target='_blank' href={peerMeta.url} rel='noreferrer'>{peerMeta.url}</Link>
</Row>
<Row type='flex' justify='center' align='middle' style={{ marginTop: '24px' }}>
<Button style={{ marginRight: '24px' }} onClick={approveSession}>Approve</Button>
<Button onClick={rejectSession}>Reject</Button>
</Row>
</>
)
}

const WalletConnect = ({ wc }) => {
const dispatch = useDispatch()
const wallets = useSelector(state => state.wallet)
const walletConnectSession = useSelector(state => state.cache.walletConnectSession)
const walletList = Object.keys(wallets).filter(addr => util.safeNormalizedAddress(addr))
const [selectedAddress, setSelectedAddress] = useState({ value: walletList[0] })
const [connected, setConnected] = useState(false)
const [loading, setLoading] = useState(false)
// Default to QR unless wc provided.
const [isScanMode, setScanMode] = useState(!wc)
const [hasCamera, setHasCamera] = useState(false)
const [uri, setUri] = useState('')
const [connector, setConnector] = useState(null)
const [peerMeta, setPeerMeta] = useState(null)
const [requests, setRequests] = useState([])

useEffect(() => {
const f = async () => {
const [hasCamera] = await checkCamera()
setHasCamera(hasCamera)
}

f()
}, [])

useEffect(() => {
if (wc) {
initWalletConnect(wc)
}
}, [wc])

const subscribeToEvents = (connector) => {
if (connector) {
connector.on('session_request', (error, payload) => {
if (error) {
throw error
}
// dispatch(cacheActions.fetchVersion({ network }))
const { peerMeta } = payload.params[0]
setPeerMeta(peerMeta)
})

connector.on('session_update', error => {
if (error) {
throw error
}

dispatch(cacheActions.updateWalletConnectSession(connector.session))
})

connector.on('call_request', async (error, payload) => {
console.log('EVENT', 'call_request', 'method', payload.method)
if (error) {
throw error
}

setRequests(requests => ([...requests, payload]))
})

connector.on('connect', (error, payload) => {
if (error) {
throw error
}

setConnected(true)
dispatch(cacheActions.updateWalletConnectSession(connector.session))
})

connector.on('disconnect', (error, payload) => {
if (error) {
throw error
}

setConnected(false)
setLoading(false)
setConnector(null)
setPeerMeta(null)
dispatch(cacheActions.updateWalletConnectSession(null))
})

if (connector.connected) {
setConnected(true)
}

setConnector(connector)
}
}

useEffect(() => {
if (walletConnectSession) {
const connector = new WalletConnectClient({ session: walletConnectSession })
subscribeToEvents(connector)
}
}, [])

const initWalletConnect = async (uri, reportOnError = true) => {
setLoading(true)

try {
const connector = new WalletConnectClient({
uri,
clientMeta: {
description: '1Wallet',
url: 'https://1wallet.crazy.one',
icons: ['https://1wallet.crazy.one/1wallet.png'],
name: '1Wallet',
}
})

if (!connector.connected) {
await connector.createSession()
}

setLoading(false)

subscribeToEvents(connector)
} catch (error) {
if (reportOnError) {
message.error('Failed to connect.', 15)
}
setLoading(false)
}
}

const approveSession = () => {
if (connector) {
connector.approveSession({ chainId: connector.chainId, accounts: [selectedAddress.value] })
}
}

const rejectSession = () => {
if (connector) {
connector.rejectSession()
setUri('')
}
}

const approveRequest = (request) => {
console.log(request)
if (connector) {
// TODO: Handle requests based on methods
// connector.approveRequest({
// id: request.id,
// result
// })
// setRequests(requests.filter(r => r.id !== request.id))
}
}

const rejectRequest = (request) => {
console.log(request)
if (connector) {
connector.rejectRequest({ id: request.id, error: { message: 'User rejected the requets.' } })
setRequests(requests.filter(r => r.id !== request.id))
}
}

const disconnect = () => {
if (connector) {
connector.killSession()
setUri('')
}
}

const onScan = (uri) => {
initWalletConnect(uri, /* reportOnError= */ false)
}

useEffect(() => {
if (!uri) {
return
}
initWalletConnect(uri)
}, [uri])

return (
<AnimatedSection>
{loading && (
<Row type='flex' justify='center' align='middle' style={{ minHeight: '100vh' }}>
<Spin size='large' />
</Row>)}
{!connected
? (peerMeta && peerMeta.name
? <PromptView peerMeta={peerMeta} approveSession={approveSession} rejectSession={rejectSession} />
: (
<>
<WalletSelector onAddressSelected={setSelectedAddress} filter={e => e.majorVersion >= 10} showOlderVersions={false} useHex={false} />
{/* If no camera or `uri` provided, do not show the qr scanner initially. */}
{(!isScanMode || !hasCamera) && (
<>
<InputBox margin='auto' width={440} value={uri} onChange={({ target: { value } }) => setUri(value)} placeholder='Paste wc: uri...' />
{hasCamera && <Button onClick={() => setScanMode(true)}>Scan</Button>}
</>)}
{selectedAddress.value && isScanMode && hasCamera && <QrCodeScanner shouldInit uploadBtnText='Upload QR Code Instead' onScan={onScan} />}
</>))
: (
<>
<Row type='flex' justify='center' align='middle'>
<Text>{selectedAddress.value}</Text>
<Button type='link' onClick={disconnect}>Disconnect</Button>
</Row>
<ConfigProvider renderEmpty={() => (
<Text>No pending requests for this wallet.</Text>
)}
>
<List
style={{ marginTop: '24px' }}
size='large'
bordered
dataSource={requests}
// TODO: customize item render based on request method type.
renderItem={item => (
<List.Item
actions={[<Button key='request-list-action-approve' onClick={() => approveRequest(item)}>Approve</Button>, <Button key='request-list-action-reject' onClick={() => rejectRequest(item)}>Reject</Button>]}
>
{item.method}
{item.method === 'eth_sendTransaction' &&
<Send
address={selectedAddress.value} onSuccess={() => alert('a')}
prefillAmount={10}
/>}
</List.Item>)}
/>
</ConfigProvider>
</>)}
</AnimatedSection>
)
}

export default WalletConnect
5 changes: 5 additions & 0 deletions code/client/src/pages/Tools.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,11 @@ const Tools = () => {
<Button type='primary' shape='round' href='http://multisig.harmony.one' target='_blank'>Open Harmony MultiSig</Button>
<Button type='primary' shape='round' onClick={() => openTool(Sections.SushiEncoder)}>SushiSwap Transaction Encoder</Button>
</Space>
<Divider />
<Title level={3}>Auth</Title>
<Space wrap>
<Button type='primary' shape='round' href='/auth/walletconnect'>Wallet Connect</Button>
Copy link
Owner

Choose a reason for hiding this comment

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

This is quite hidden. Probably not the best place to put it. Any suggestions for better place?

</Space>
{dev &&
<>
<Divider />
Expand Down
4 changes: 3 additions & 1 deletion code/client/src/state/modules/cache/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const updateVersion = createAction('UPDATE_VERSION')
const updateClientVersion = createAction('UPDATE_CLIENT_VERSION')
const fetchGlobalStats = createAction('FETCH_GLOBAL_STATS')
const updateGlobalStats = createAction('UPDATE_GLOBAL_STATS')
const updateWalletConnectSession = createAction('UPDATE_WALLET_CONNECT_SESSION')

export default {
fetchCode,
Expand All @@ -16,5 +17,6 @@ export default {
updateCode,
clearCode,
fetchGlobalStats,
updateGlobalStats
updateGlobalStats,
updateWalletConnectSession
}
5 changes: 5 additions & 0 deletions code/client/src/state/modules/cache/reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const initialState = {
version: {},
clientVersion: '',
needCodeUpdate: true,
walletConnectSession: null,
}

const reducer = handleActions(
Expand Down Expand Up @@ -43,6 +44,10 @@ const reducer = handleActions(
...state,
global: { ...state.global, stats: action.payload }
}),
[cacheActions.updateWalletConnectSession]: (state, action) => ({
...state,
walletConnectSession: action.payload
}),
},
{
...initialState
Expand Down
Loading