Implement multi wallet support
This commit is contained in:
parent
c69121d673
commit
e82ca094b3
@ -1,17 +1,18 @@
|
|||||||
import SettingsStore from '@/store/SettingsStore'
|
import SettingsStore from '@/store/SettingsStore'
|
||||||
|
import { addresses } from '@/utils/WalletUtil'
|
||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
|
|
||||||
export default function AccountPicker() {
|
export default function AccountPicker() {
|
||||||
const { account } = useSnapshot(SettingsStore.state)
|
const { address } = useSnapshot(SettingsStore.state)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<select
|
<select
|
||||||
value={account}
|
value={address}
|
||||||
onChange={e => SettingsStore.setAccount(e.currentTarget.value)}
|
onChange={e => SettingsStore.setAddress(e.currentTarget.value)}
|
||||||
aria-label="accounts"
|
aria-label="addresses"
|
||||||
>
|
>
|
||||||
<option value={0}>Account 1</option>
|
<option value={addresses[0]}>Account 1</option>
|
||||||
<option value={1}>Account 2</option>
|
<option value={addresses[1]}>Account 2</option>
|
||||||
</select>
|
</select>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import SettingsStore from '@/store/SettingsStore'
|
||||||
import { createWalletConnectClient } from '@/utils/WalletConnectUtil'
|
import { createWalletConnectClient } from '@/utils/WalletConnectUtil'
|
||||||
import { createOrRestoreWallet } from '@/utils/WalletUtil'
|
import { createOrRestoreWallet } from '@/utils/WalletUtil'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
@ -7,7 +8,8 @@ export default function useInitialization() {
|
|||||||
|
|
||||||
const onInitialize = useCallback(async () => {
|
const onInitialize = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
createOrRestoreWallet()
|
const { addresses } = createOrRestoreWallet()
|
||||||
|
SettingsStore.setAddress(addresses[0])
|
||||||
await createWalletConnectClient()
|
await createWalletConnectClient()
|
||||||
setInitialized(true)
|
setInitialized(true)
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
|
@ -3,14 +3,12 @@ import AccountPicker from '@/components/AccountPicker'
|
|||||||
import PageHeader from '@/components/PageHeader'
|
import PageHeader from '@/components/PageHeader'
|
||||||
import { EIP155_MAINNET_CHAINS, EIP155_TEST_CHAINS } from '@/data/EIP155Data'
|
import { EIP155_MAINNET_CHAINS, EIP155_TEST_CHAINS } from '@/data/EIP155Data'
|
||||||
import SettingsStore from '@/store/SettingsStore'
|
import SettingsStore from '@/store/SettingsStore'
|
||||||
import { wallets } from '@/utils/WalletUtil'
|
|
||||||
import { Text } from '@nextui-org/react'
|
import { Text } from '@nextui-org/react'
|
||||||
import { Fragment } from 'react'
|
import { Fragment } from 'react'
|
||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
|
|
||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
const { testNets, account } = useSnapshot(SettingsStore.state)
|
const { testNets, address } = useSnapshot(SettingsStore.state)
|
||||||
const addresses = Object.keys(wallets)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
@ -21,7 +19,7 @@ export default function HomePage() {
|
|||||||
Mainnets
|
Mainnets
|
||||||
</Text>
|
</Text>
|
||||||
{Object.values(EIP155_MAINNET_CHAINS).map(({ name, logo, rgb }) => (
|
{Object.values(EIP155_MAINNET_CHAINS).map(({ name, logo, rgb }) => (
|
||||||
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={addresses[account]} />
|
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={address} />
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{testNets ? (
|
{testNets ? (
|
||||||
@ -30,13 +28,7 @@ export default function HomePage() {
|
|||||||
Testnets
|
Testnets
|
||||||
</Text>
|
</Text>
|
||||||
{Object.values(EIP155_TEST_CHAINS).map(({ name, logo, rgb }) => (
|
{Object.values(EIP155_TEST_CHAINS).map(({ name, logo, rgb }) => (
|
||||||
<AccountCard
|
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={address} />
|
||||||
key={name}
|
|
||||||
name={name}
|
|
||||||
logo={logo}
|
|
||||||
rgb={rgb}
|
|
||||||
address={addresses[account]}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -6,7 +6,7 @@ import { Fragment } from 'react'
|
|||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
|
|
||||||
export default function SettingsPage() {
|
export default function SettingsPage() {
|
||||||
const { testNets } = useSnapshot(SettingsStore.state)
|
const { testNets, address } = useSnapshot(SettingsStore.state)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
@ -15,9 +15,7 @@ export default function SettingsPage() {
|
|||||||
Mnemonic
|
Mnemonic
|
||||||
</Text>
|
</Text>
|
||||||
<Card bordered borderWeight="light" css={{ minHeight: '75px' }}>
|
<Card bordered borderWeight="light" css={{ minHeight: '75px' }}>
|
||||||
<Text css={{ fontFamily: '$mono' }}>
|
<Text css={{ fontFamily: '$mono' }}>{wallets[address].mnemonic.phrase}</Text>
|
||||||
{wallets['0xD0712a5018b6F3401b90Cd75C15d95B3353a4088'].mnemonic.phrase}
|
|
||||||
</Text>
|
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Text css={{ color: '$yellow500', marginTop: '$5', textAlign: 'center' }}>
|
<Text css={{ color: '$yellow500', marginTop: '$5', textAlign: 'center' }}>
|
||||||
|
@ -5,7 +5,7 @@ import { proxy } from 'valtio'
|
|||||||
*/
|
*/
|
||||||
interface State {
|
interface State {
|
||||||
testNets: boolean
|
testNets: boolean
|
||||||
account: number
|
address: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -13,7 +13,7 @@ interface State {
|
|||||||
*/
|
*/
|
||||||
const state = proxy<State>({
|
const state = proxy<State>({
|
||||||
testNets: typeof localStorage !== 'undefined' ? Boolean(localStorage.getItem('TEST_NETS')) : true,
|
testNets: typeof localStorage !== 'undefined' ? Boolean(localStorage.getItem('TEST_NETS')) : true,
|
||||||
account: 0
|
address: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,8 +22,8 @@ const state = proxy<State>({
|
|||||||
const SettingsStore = {
|
const SettingsStore = {
|
||||||
state,
|
state,
|
||||||
|
|
||||||
setAccount(value: number | string) {
|
setAddress(address: string) {
|
||||||
state.account = Number(value)
|
state.address = address
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleTestNets() {
|
toggleTestNets() {
|
||||||
|
@ -52,3 +52,20 @@ export function getSignTypedDataParamsData(params: string[]) {
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get our address from params checking if params string contains one
|
||||||
|
* of our wallet addresses
|
||||||
|
*/
|
||||||
|
export function getWalletAddressFromParams(addresses: string[], params: any) {
|
||||||
|
const paramsString = JSON.stringify(params)
|
||||||
|
let address = ''
|
||||||
|
|
||||||
|
addresses.forEach(addr => {
|
||||||
|
if (paramsString.includes(addr)) {
|
||||||
|
address = addr
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return address
|
||||||
|
}
|
||||||
|
@ -1,12 +1,19 @@
|
|||||||
import { EIP155_SIGNING_METHODS } from '@/data/EIP155Data'
|
import { EIP155_CHAINS, EIP155_SIGNING_METHODS, TEIP155Chain } from '@/data/EIP155Data'
|
||||||
import { getSignParamsMessage, getSignTypedDataParamsData } from '@/utils/HelperUtil'
|
import {
|
||||||
|
getSignParamsMessage,
|
||||||
|
getSignTypedDataParamsData,
|
||||||
|
getWalletAddressFromParams
|
||||||
|
} from '@/utils/HelperUtil'
|
||||||
|
import { addresses, wallets } from '@/utils/WalletUtil'
|
||||||
import { formatJsonRpcError, formatJsonRpcResult } from '@json-rpc-tools/utils'
|
import { formatJsonRpcError, formatJsonRpcResult } from '@json-rpc-tools/utils'
|
||||||
import { RequestEvent } from '@walletconnect/types'
|
import { RequestEvent } from '@walletconnect/types'
|
||||||
import { ERROR } from '@walletconnect/utils'
|
import { ERROR } from '@walletconnect/utils'
|
||||||
import { Wallet } from 'ethers'
|
import { providers } from 'ethers'
|
||||||
|
|
||||||
export async function approveEIP155Request(request: RequestEvent['request'], wallet: Wallet) {
|
export async function approveEIP155Request(requestEvent: RequestEvent) {
|
||||||
const { method, params, id } = request
|
const { method, params, id } = requestEvent.request
|
||||||
|
const { chainId } = requestEvent
|
||||||
|
const wallet = wallets[getWalletAddressFromParams(addresses, params)]
|
||||||
|
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case EIP155_SIGNING_METHODS.PERSONAL_SIGN:
|
case EIP155_SIGNING_METHODS.PERSONAL_SIGN:
|
||||||
@ -25,8 +32,10 @@ export async function approveEIP155Request(request: RequestEvent['request'], wal
|
|||||||
return formatJsonRpcResult(id, signedData)
|
return formatJsonRpcResult(id, signedData)
|
||||||
|
|
||||||
case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION:
|
case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION:
|
||||||
|
const provider = new providers.JsonRpcProvider(EIP155_CHAINS[chainId as TEIP155Chain].rpc)
|
||||||
const sendTransaction = params[0]
|
const sendTransaction = params[0]
|
||||||
const { hash } = await wallet.sendTransaction(sendTransaction)
|
const connectedWallet = wallet.connect(provider)
|
||||||
|
const { hash } = await connectedWallet.sendTransaction(sendTransaction)
|
||||||
return formatJsonRpcResult(id, hash)
|
return formatJsonRpcResult(id, hash)
|
||||||
|
|
||||||
case EIP155_SIGNING_METHODS.ETH_SIGN_TRANSACTION:
|
case EIP155_SIGNING_METHODS.ETH_SIGN_TRANSACTION:
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Wallet } from 'ethers'
|
import { Wallet } from 'ethers'
|
||||||
|
|
||||||
export let wallets: Record<string, Wallet>
|
export let wallets: Record<string, Wallet>
|
||||||
|
export let addresses: string[]
|
||||||
export let wallet1: Wallet
|
export let wallet1: Wallet
|
||||||
export let wallet2: Wallet
|
export let wallet2: Wallet
|
||||||
|
|
||||||
@ -21,4 +22,10 @@ export function createOrRestoreWallet() {
|
|||||||
[wallet1.address]: wallet1,
|
[wallet1.address]: wallet1,
|
||||||
[wallet2.address]: wallet2
|
[wallet2.address]: wallet2
|
||||||
}
|
}
|
||||||
|
addresses = Object.keys(wallets)
|
||||||
|
|
||||||
|
return {
|
||||||
|
wallets,
|
||||||
|
addresses
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,25 @@
|
|||||||
import { EIP155_CHAINS, TEIP155Chain } from '@/data/EIP155Data'
|
import { EIP155_CHAINS, TEIP155Chain } from '@/data/EIP155Data'
|
||||||
import ModalStore from '@/store/ModalStore'
|
import ModalStore from '@/store/ModalStore'
|
||||||
|
import { truncate } from '@/utils/HelperUtil'
|
||||||
import { walletConnectClient } from '@/utils/WalletConnectUtil'
|
import { walletConnectClient } from '@/utils/WalletConnectUtil'
|
||||||
import { wallets } from '@/utils/WalletUtil'
|
import { addresses } from '@/utils/WalletUtil'
|
||||||
import { Avatar, Button, Col, Container, Divider, Link, Modal, Row, Text } from '@nextui-org/react'
|
import {
|
||||||
import { Fragment } from 'react'
|
Avatar,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Col,
|
||||||
|
Container,
|
||||||
|
Divider,
|
||||||
|
Link,
|
||||||
|
Modal,
|
||||||
|
Row,
|
||||||
|
Text
|
||||||
|
} from '@nextui-org/react'
|
||||||
|
import { Fragment, useState } from 'react'
|
||||||
|
|
||||||
export default function SessionProposalModal() {
|
export default function SessionProposalModal() {
|
||||||
|
const [selectedAddresses, setSelectedAddresses] = useState<string[]>([])
|
||||||
|
|
||||||
// Get proposal data and wallet address from store
|
// Get proposal data and wallet address from store
|
||||||
const proposal = ModalStore.state.data?.proposal
|
const proposal = ModalStore.state.data?.proposal
|
||||||
|
|
||||||
@ -21,14 +35,29 @@ export default function SessionProposalModal() {
|
|||||||
const { methods } = permissions.jsonrpc
|
const { methods } = permissions.jsonrpc
|
||||||
const { protocol } = relay
|
const { protocol } = relay
|
||||||
|
|
||||||
|
// Add / remove address from selection
|
||||||
|
function onSelectAddress(address: string) {
|
||||||
|
if (selectedAddresses.includes(address)) {
|
||||||
|
const newAddresses = selectedAddresses.filter(a => a !== address)
|
||||||
|
setSelectedAddresses(newAddresses)
|
||||||
|
} else {
|
||||||
|
setSelectedAddresses([...selectedAddresses, address])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Hanlde approve action
|
// Hanlde approve action
|
||||||
async function onApprove() {
|
async function onApprove() {
|
||||||
if (proposal) {
|
if (proposal) {
|
||||||
|
const accounts: string[] = []
|
||||||
|
chains.forEach(chain => {
|
||||||
|
selectedAddresses.forEach(address => {
|
||||||
|
accounts.push(`${chain}:${address}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
const response = {
|
const response = {
|
||||||
state: {
|
state: {
|
||||||
accounts: chains.map(
|
accounts
|
||||||
chain => `${chain}:${wallets['0xD0712a5018b6F3401b90Cd75C15d95B3353a4088'].address}`
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await walletConnectClient.approve({ proposal, response })
|
await walletConnectClient.approve({ proposal, response })
|
||||||
@ -92,6 +121,27 @@ export default function SessionProposalModal() {
|
|||||||
<Text color="$gray400">{protocol}</Text>
|
<Text color="$gray400">{protocol}</Text>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
|
<Divider y={2} />
|
||||||
|
|
||||||
|
<Row>
|
||||||
|
<Col>
|
||||||
|
<Text h5>Select Accounts to Connect</Text>
|
||||||
|
{addresses.map((address, index) => (
|
||||||
|
<Card
|
||||||
|
onClick={() => onSelectAddress(address)}
|
||||||
|
clickable
|
||||||
|
key={address}
|
||||||
|
css={{
|
||||||
|
marginTop: '$5',
|
||||||
|
backgroundColor: selectedAddresses.includes(address) ? '$green600' : '$accents2'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text>{`Acc ${index + 1} - ${truncate(address, 19)}`}</Text>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
</Container>
|
</Container>
|
||||||
</Modal.Body>
|
</Modal.Body>
|
||||||
|
|
||||||
@ -99,7 +149,14 @@ export default function SessionProposalModal() {
|
|||||||
<Button auto flat color="error" onClick={onReject}>
|
<Button auto flat color="error" onClick={onReject}>
|
||||||
Reject
|
Reject
|
||||||
</Button>
|
</Button>
|
||||||
<Button auto flat color="success" onClick={onApprove}>
|
<Button
|
||||||
|
auto
|
||||||
|
flat
|
||||||
|
color="success"
|
||||||
|
onClick={onApprove}
|
||||||
|
disabled={!selectedAddresses.length}
|
||||||
|
css={{ opacity: selectedAddresses.length ? 1 : 0.4 }}
|
||||||
|
>
|
||||||
Approve
|
Approve
|
||||||
</Button>
|
</Button>
|
||||||
</Modal.Footer>
|
</Modal.Footer>
|
||||||
|
@ -3,7 +3,6 @@ import ModalStore from '@/store/ModalStore'
|
|||||||
import { truncate } from '@/utils/HelperUtil'
|
import { truncate } from '@/utils/HelperUtil'
|
||||||
import { approveEIP155Request, rejectEIP155Request } from '@/utils/RequestHandlerUtil'
|
import { approveEIP155Request, rejectEIP155Request } from '@/utils/RequestHandlerUtil'
|
||||||
import { walletConnectClient } from '@/utils/WalletConnectUtil'
|
import { walletConnectClient } from '@/utils/WalletConnectUtil'
|
||||||
import { wallets } from '@/utils/WalletUtil'
|
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
Button,
|
Button,
|
||||||
@ -16,7 +15,6 @@ import {
|
|||||||
Row,
|
Row,
|
||||||
Text
|
Text
|
||||||
} from '@nextui-org/react'
|
} from '@nextui-org/react'
|
||||||
import { providers } from 'ethers'
|
|
||||||
import { Fragment, useState } from 'react'
|
import { Fragment, useState } from 'react'
|
||||||
|
|
||||||
export default function SessionSendTransactionModal() {
|
export default function SessionSendTransactionModal() {
|
||||||
@ -42,10 +40,7 @@ export default function SessionSendTransactionModal() {
|
|||||||
async function onApprove() {
|
async function onApprove() {
|
||||||
if (requestEvent) {
|
if (requestEvent) {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
const provider = new providers.JsonRpcProvider(EIP155_CHAINS[chainId as TEIP155Chain].rpc)
|
const response = await approveEIP155Request(requestEvent)
|
||||||
const connectedWallet =
|
|
||||||
wallets['0xD0712a5018b6F3401b90Cd75C15d95B3353a4088'].connect(provider)
|
|
||||||
const response = await approveEIP155Request(requestEvent.request, connectedWallet)
|
|
||||||
await walletConnectClient.respond({
|
await walletConnectClient.respond({
|
||||||
topic: requestEvent.topic,
|
topic: requestEvent.topic,
|
||||||
response
|
response
|
||||||
|
@ -3,7 +3,6 @@ import ModalStore from '@/store/ModalStore'
|
|||||||
import { getSignParamsMessage } from '@/utils/HelperUtil'
|
import { getSignParamsMessage } from '@/utils/HelperUtil'
|
||||||
import { approveEIP155Request, rejectEIP155Request } from '@/utils/RequestHandlerUtil'
|
import { approveEIP155Request, rejectEIP155Request } from '@/utils/RequestHandlerUtil'
|
||||||
import { walletConnectClient } from '@/utils/WalletConnectUtil'
|
import { walletConnectClient } from '@/utils/WalletConnectUtil'
|
||||||
import { wallets } from '@/utils/WalletUtil'
|
|
||||||
import { Avatar, Button, Col, Container, Divider, Link, Modal, Row, Text } from '@nextui-org/react'
|
import { Avatar, Button, Col, Container, Divider, Link, Modal, Row, Text } from '@nextui-org/react'
|
||||||
import { Fragment } from 'react'
|
import { Fragment } from 'react'
|
||||||
|
|
||||||
@ -29,10 +28,7 @@ export default function SessionSignModal() {
|
|||||||
// Handle approve action (logic varies based on request method)
|
// Handle approve action (logic varies based on request method)
|
||||||
async function onApprove() {
|
async function onApprove() {
|
||||||
if (requestEvent) {
|
if (requestEvent) {
|
||||||
const response = await approveEIP155Request(
|
const response = await approveEIP155Request(requestEvent)
|
||||||
requestEvent.request,
|
|
||||||
wallets['0xD0712a5018b6F3401b90Cd75C15d95B3353a4088']
|
|
||||||
)
|
|
||||||
await walletConnectClient.respond({
|
await walletConnectClient.respond({
|
||||||
topic: requestEvent.topic,
|
topic: requestEvent.topic,
|
||||||
response
|
response
|
||||||
|
@ -3,7 +3,6 @@ import ModalStore from '@/store/ModalStore'
|
|||||||
import { getSignTypedDataParamsData } from '@/utils/HelperUtil'
|
import { getSignTypedDataParamsData } from '@/utils/HelperUtil'
|
||||||
import { approveEIP155Request, rejectEIP155Request } from '@/utils/RequestHandlerUtil'
|
import { approveEIP155Request, rejectEIP155Request } from '@/utils/RequestHandlerUtil'
|
||||||
import { walletConnectClient } from '@/utils/WalletConnectUtil'
|
import { walletConnectClient } from '@/utils/WalletConnectUtil'
|
||||||
import { wallets } from '@/utils/WalletUtil'
|
|
||||||
import { Avatar, Button, Col, Container, Divider, Link, Modal, Row, Text } from '@nextui-org/react'
|
import { Avatar, Button, Col, Container, Divider, Link, Modal, Row, Text } from '@nextui-org/react'
|
||||||
import { Fragment } from 'react'
|
import { Fragment } from 'react'
|
||||||
import { CodeBlock, codepen } from 'react-code-blocks'
|
import { CodeBlock, codepen } from 'react-code-blocks'
|
||||||
@ -30,10 +29,7 @@ export default function SessionSignTypedDataModal() {
|
|||||||
// Handle approve action (logic varies based on request method)
|
// Handle approve action (logic varies based on request method)
|
||||||
async function onApprove() {
|
async function onApprove() {
|
||||||
if (requestEvent) {
|
if (requestEvent) {
|
||||||
const response = await approveEIP155Request(
|
const response = await approveEIP155Request(requestEvent)
|
||||||
requestEvent.request,
|
|
||||||
wallets['0xD0712a5018b6F3401b90Cd75C15d95B3353a4088']
|
|
||||||
)
|
|
||||||
await walletConnectClient.respond({
|
await walletConnectClient.respond({
|
||||||
topic: requestEvent.topic,
|
topic: requestEvent.topic,
|
||||||
response
|
response
|
||||||
|
Loading…
Reference in New Issue
Block a user