Merge branch 'wallets'
This commit is contained in:
commit
c5c6823858
3
wallets/react-wallet-v2/public/icons/arrow-down-icon.svg
Normal file
3
wallets/react-wallet-v2/public/icons/arrow-down-icon.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M112 184L256 328L400 184" stroke="white" stroke-width="48" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 224 B |
@ -57,4 +57,25 @@
|
||||
|
||||
.navLink:hover {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
select {
|
||||
background-color: rgba(139, 139, 139, 0.2);
|
||||
background-image: url(/icons/arrow-down-icon.svg);
|
||||
background-size: 15px 15px;
|
||||
background-position: right 10px center;
|
||||
background-repeat: no-repeat;
|
||||
padding: 5px 30px 6px 10px;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
appearance: none;
|
||||
transition: .2s ease-in-out background-color;
|
||||
font-family: var(--nextui-fonts-sans);
|
||||
font-weight: var(--nextui-fontWeights-light);
|
||||
border: 1px solid rgba(139, 139, 139, 0.25);
|
||||
}
|
||||
|
||||
select:hover {
|
||||
background-color: rgba(139, 139, 139, 0.35);
|
||||
}
|
18
wallets/react-wallet-v2/src/components/AccountPicker.tsx
Normal file
18
wallets/react-wallet-v2/src/components/AccountPicker.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import SettingsStore from '@/store/SettingsStore'
|
||||
import { addresses } from '@/utils/WalletUtil'
|
||||
import { useSnapshot } from 'valtio'
|
||||
|
||||
export default function AccountPicker() {
|
||||
const { address } = useSnapshot(SettingsStore.state)
|
||||
|
||||
return (
|
||||
<select
|
||||
value={address}
|
||||
onChange={e => SettingsStore.setAddress(e.currentTarget.value)}
|
||||
aria-label="addresses"
|
||||
>
|
||||
<option value={addresses[0]}>Account 1</option>
|
||||
<option value={addresses[1]}>Account 2</option>
|
||||
</select>
|
||||
)
|
||||
}
|
@ -1,29 +1,35 @@
|
||||
import { Divider, Text } from '@nextui-org/react'
|
||||
import { Fragment } from 'react'
|
||||
import { Col, Divider, Row, Text } from '@nextui-org/react'
|
||||
import { Fragment, ReactNode } from 'react'
|
||||
|
||||
/**
|
||||
* Types
|
||||
*/
|
||||
interface Props {
|
||||
children: string
|
||||
children?: ReactNode | ReactNode[]
|
||||
title: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Component
|
||||
*/
|
||||
export default function PageHeader({ children }: Props) {
|
||||
export default function PageHeader({ title, children }: Props) {
|
||||
return (
|
||||
<Fragment>
|
||||
<Text
|
||||
h3
|
||||
weight="bold"
|
||||
css={{
|
||||
textGradient: '90deg, $secondary, $primary 30%',
|
||||
marginBottom: '$5'
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Text>
|
||||
<Row css={{ marginBottom: '$5', width: '100%' }} justify="space-between" align="center">
|
||||
<Col>
|
||||
<Text
|
||||
h3
|
||||
weight="bold"
|
||||
css={{
|
||||
textGradient: '90deg, $secondary, $primary 30%'
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
</Col>
|
||||
{children ? <Col css={{ flex: 1 }}>{children}</Col> : null}
|
||||
</Row>
|
||||
|
||||
<Divider css={{ marginBottom: '$10' }} />
|
||||
</Fragment>
|
||||
)
|
||||
|
@ -1,3 +1,4 @@
|
||||
import SettingsStore from '@/store/SettingsStore'
|
||||
import { createWalletConnectClient } from '@/utils/WalletConnectUtil'
|
||||
import { createOrRestoreWallet } from '@/utils/WalletUtil'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
@ -7,7 +8,8 @@ export default function useInitialization() {
|
||||
|
||||
const onInitialize = useCallback(async () => {
|
||||
try {
|
||||
createOrRestoreWallet()
|
||||
const { addresses } = createOrRestoreWallet()
|
||||
SettingsStore.setAddress(addresses[0])
|
||||
await createWalletConnectClient()
|
||||
setInitialized(true)
|
||||
} catch (err: unknown) {
|
||||
|
@ -1,23 +1,25 @@
|
||||
import AccountCard from '@/components/AccountCard'
|
||||
import AccountPicker from '@/components/AccountPicker'
|
||||
import PageHeader from '@/components/PageHeader'
|
||||
import { EIP155_MAINNET_CHAINS, EIP155_TEST_CHAINS } from '@/data/EIP155Data'
|
||||
import SettingsStore from '@/store/SettingsStore'
|
||||
import { wallet } from '@/utils/WalletUtil'
|
||||
import { Text } from '@nextui-org/react'
|
||||
import { Fragment } from 'react'
|
||||
import { useSnapshot } from 'valtio'
|
||||
|
||||
export default function HomePage() {
|
||||
const { testNets } = useSnapshot(SettingsStore.state)
|
||||
const { testNets, address } = useSnapshot(SettingsStore.state)
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<PageHeader>Accounts</PageHeader>
|
||||
<PageHeader title="Accounts">
|
||||
<AccountPicker />
|
||||
</PageHeader>
|
||||
<Text h4 css={{ marginBottom: '$5' }}>
|
||||
Mainnets
|
||||
</Text>
|
||||
{Object.values(EIP155_MAINNET_CHAINS).map(({ name, logo, rgb }) => (
|
||||
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={wallet.address} />
|
||||
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={address} />
|
||||
))}
|
||||
|
||||
{testNets ? (
|
||||
@ -26,7 +28,7 @@ export default function HomePage() {
|
||||
Testnets
|
||||
</Text>
|
||||
{Object.values(EIP155_TEST_CHAINS).map(({ name, logo, rgb }) => (
|
||||
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={wallet.address} />
|
||||
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={address} />
|
||||
))}
|
||||
</Fragment>
|
||||
) : null}
|
||||
|
@ -19,7 +19,7 @@ export default function PairingsPage() {
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<PageHeader>Pairings</PageHeader>
|
||||
<PageHeader title="Pairings" />
|
||||
{pairings.length ? (
|
||||
pairings.map(pairing => {
|
||||
const { metadata } = pairing.state
|
||||
|
@ -19,7 +19,7 @@ export default function SessionsPage() {
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<PageHeader>Sessions</PageHeader>
|
||||
<PageHeader title="Sessions" />
|
||||
{sessions.length ? (
|
||||
sessions.map(session => {
|
||||
const { name, icons, url } = session.peer.metadata
|
||||
|
@ -1,21 +1,21 @@
|
||||
import PageHeader from '@/components/PageHeader'
|
||||
import SettingsStore from '@/store/SettingsStore'
|
||||
import { wallet } from '@/utils/WalletUtil'
|
||||
import { wallets } from '@/utils/WalletUtil'
|
||||
import { Card, Divider, Row, Switch, Text } from '@nextui-org/react'
|
||||
import { Fragment } from 'react'
|
||||
import { useSnapshot } from 'valtio'
|
||||
|
||||
export default function SettingsPage() {
|
||||
const { testNets } = useSnapshot(SettingsStore.state)
|
||||
const { testNets, address } = useSnapshot(SettingsStore.state)
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<PageHeader>Settings</PageHeader>
|
||||
<PageHeader title="Settings" />
|
||||
<Text h4 css={{ marginBottom: '$5' }}>
|
||||
Mnemonic
|
||||
</Text>
|
||||
<Card bordered borderWeight="light" css={{ minHeight: '75px' }}>
|
||||
<Text css={{ fontFamily: '$mono' }}>{wallet.mnemonic.phrase}</Text>
|
||||
<Text css={{ fontFamily: '$mono' }}>{wallets[address].mnemonic.phrase}</Text>
|
||||
</Card>
|
||||
|
||||
<Text css={{ color: '$yellow500', marginTop: '$5', textAlign: 'center' }}>
|
||||
|
@ -22,7 +22,7 @@ export default function WalletConnectPage() {
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<PageHeader>WalletConnect</PageHeader>
|
||||
<PageHeader title="WalletConnect" />
|
||||
|
||||
<QrReader onConnect={onConnect} />
|
||||
|
||||
|
@ -5,13 +5,15 @@ import { proxy } from 'valtio'
|
||||
*/
|
||||
interface State {
|
||||
testNets: boolean
|
||||
address: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
address: ''
|
||||
})
|
||||
|
||||
/**
|
||||
@ -20,6 +22,10 @@ const state = proxy<State>({
|
||||
const SettingsStore = {
|
||||
state,
|
||||
|
||||
setAddress(address: string) {
|
||||
state.address = address
|
||||
},
|
||||
|
||||
toggleTestNets() {
|
||||
state.testNets = !state.testNets
|
||||
if (state.testNets) {
|
||||
|
@ -52,3 +52,20 @@ export function getSignTypedDataParamsData(params: string[]) {
|
||||
|
||||
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 { getSignParamsMessage, getSignTypedDataParamsData } from '@/utils/HelperUtil'
|
||||
import { EIP155_CHAINS, EIP155_SIGNING_METHODS, TEIP155Chain } from '@/data/EIP155Data'
|
||||
import {
|
||||
getSignParamsMessage,
|
||||
getSignTypedDataParamsData,
|
||||
getWalletAddressFromParams
|
||||
} from '@/utils/HelperUtil'
|
||||
import { addresses, wallets } from '@/utils/WalletUtil'
|
||||
import { formatJsonRpcError, formatJsonRpcResult } from '@json-rpc-tools/utils'
|
||||
import { RequestEvent } from '@walletconnect/types'
|
||||
import { ERROR } from '@walletconnect/utils'
|
||||
import { Wallet } from 'ethers'
|
||||
import { providers } from 'ethers'
|
||||
|
||||
export async function approveEIP155Request(request: RequestEvent['request'], wallet: Wallet) {
|
||||
const { method, params, id } = request
|
||||
export async function approveEIP155Request(requestEvent: RequestEvent) {
|
||||
const { method, params, id } = requestEvent.request
|
||||
const { chainId } = requestEvent
|
||||
const wallet = wallets[getWalletAddressFromParams(addresses, params)]
|
||||
|
||||
switch (method) {
|
||||
case EIP155_SIGNING_METHODS.PERSONAL_SIGN:
|
||||
@ -25,8 +32,10 @@ export async function approveEIP155Request(request: RequestEvent['request'], wal
|
||||
return formatJsonRpcResult(id, signedData)
|
||||
|
||||
case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION:
|
||||
const provider = new providers.JsonRpcProvider(EIP155_CHAINS[chainId as TEIP155Chain].rpc)
|
||||
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)
|
||||
|
||||
case EIP155_SIGNING_METHODS.ETH_SIGN_TRANSACTION:
|
||||
|
@ -1,15 +1,31 @@
|
||||
import { Wallet } from 'ethers'
|
||||
|
||||
export let wallet: Wallet
|
||||
export let wallets: Record<string, Wallet>
|
||||
export let addresses: string[]
|
||||
export let wallet1: Wallet
|
||||
export let wallet2: Wallet
|
||||
|
||||
export function createOrRestoreWallet() {
|
||||
const mnemonic = localStorage.getItem('WALLET_MNEMONIC')
|
||||
|
||||
if (mnemonic) {
|
||||
wallet = Wallet.fromMnemonic(mnemonic)
|
||||
wallet1 = Wallet.fromMnemonic(mnemonic, "m/44'/60'/0'/0/0")
|
||||
wallet2 = Wallet.fromMnemonic(mnemonic, "m/44'/60'/0'/0/1")
|
||||
} else {
|
||||
wallet = Wallet.createRandom()
|
||||
wallet1 = Wallet.createRandom()
|
||||
wallet2 = Wallet.fromMnemonic(wallet1.mnemonic.phrase, "m/44'/60'/0'/0/1")
|
||||
// Don't store mnemonic in local storage in a production project!
|
||||
localStorage.setItem('WALLET_MNEMONIC', wallet.mnemonic.phrase)
|
||||
localStorage.setItem('WALLET_MNEMONIC', wallet1.mnemonic.phrase)
|
||||
}
|
||||
|
||||
wallets = {
|
||||
[wallet1.address]: wallet1,
|
||||
[wallet2.address]: wallet2
|
||||
}
|
||||
addresses = Object.keys(wallets)
|
||||
|
||||
return {
|
||||
wallets,
|
||||
addresses
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,25 @@
|
||||
import { EIP155_CHAINS, TEIP155Chain } from '@/data/EIP155Data'
|
||||
import ModalStore from '@/store/ModalStore'
|
||||
import { truncate } from '@/utils/HelperUtil'
|
||||
import { walletConnectClient } from '@/utils/WalletConnectUtil'
|
||||
import { wallet } from '@/utils/WalletUtil'
|
||||
import { Avatar, Button, Col, Container, Divider, Link, Modal, Row, Text } from '@nextui-org/react'
|
||||
import { Fragment } from 'react'
|
||||
import { addresses } from '@/utils/WalletUtil'
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
Container,
|
||||
Divider,
|
||||
Link,
|
||||
Modal,
|
||||
Row,
|
||||
Text
|
||||
} from '@nextui-org/react'
|
||||
import { Fragment, useState } from 'react'
|
||||
|
||||
export default function SessionProposalModal() {
|
||||
const [selectedAddresses, setSelectedAddresses] = useState<string[]>([])
|
||||
|
||||
// Get proposal data and wallet address from store
|
||||
const proposal = ModalStore.state.data?.proposal
|
||||
|
||||
@ -21,12 +35,29 @@ export default function SessionProposalModal() {
|
||||
const { methods } = permissions.jsonrpc
|
||||
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
|
||||
async function onApprove() {
|
||||
if (proposal) {
|
||||
const accounts: string[] = []
|
||||
chains.forEach(chain => {
|
||||
selectedAddresses.forEach(address => {
|
||||
accounts.push(`${chain}:${address}`)
|
||||
})
|
||||
})
|
||||
|
||||
const response = {
|
||||
state: {
|
||||
accounts: chains.map(chain => `${chain}:${wallet.address}`)
|
||||
accounts
|
||||
}
|
||||
}
|
||||
await walletConnectClient.approve({ proposal, response })
|
||||
@ -90,6 +121,27 @@ export default function SessionProposalModal() {
|
||||
<Text color="$gray400">{protocol}</Text>
|
||||
</Col>
|
||||
</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>
|
||||
</Modal.Body>
|
||||
|
||||
@ -97,7 +149,14 @@ export default function SessionProposalModal() {
|
||||
<Button auto flat color="error" onClick={onReject}>
|
||||
Reject
|
||||
</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
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
|
@ -3,7 +3,6 @@ import ModalStore from '@/store/ModalStore'
|
||||
import { truncate } from '@/utils/HelperUtil'
|
||||
import { approveEIP155Request, rejectEIP155Request } from '@/utils/RequestHandlerUtil'
|
||||
import { walletConnectClient } from '@/utils/WalletConnectUtil'
|
||||
import { wallet } from '@/utils/WalletUtil'
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
@ -16,7 +15,6 @@ import {
|
||||
Row,
|
||||
Text
|
||||
} from '@nextui-org/react'
|
||||
import { providers } from 'ethers'
|
||||
import { Fragment, useState } from 'react'
|
||||
|
||||
export default function SessionSendTransactionModal() {
|
||||
@ -42,9 +40,7 @@ export default function SessionSendTransactionModal() {
|
||||
async function onApprove() {
|
||||
if (requestEvent) {
|
||||
setLoading(true)
|
||||
const provider = new providers.JsonRpcProvider(EIP155_CHAINS[chainId as TEIP155Chain].rpc)
|
||||
const connectedWallet = wallet.connect(provider)
|
||||
const response = await approveEIP155Request(requestEvent.request, connectedWallet)
|
||||
const response = await approveEIP155Request(requestEvent)
|
||||
await walletConnectClient.respond({
|
||||
topic: requestEvent.topic,
|
||||
response
|
||||
|
@ -3,7 +3,6 @@ import ModalStore from '@/store/ModalStore'
|
||||
import { getSignParamsMessage } from '@/utils/HelperUtil'
|
||||
import { approveEIP155Request, rejectEIP155Request } from '@/utils/RequestHandlerUtil'
|
||||
import { walletConnectClient } from '@/utils/WalletConnectUtil'
|
||||
import { wallet } from '@/utils/WalletUtil'
|
||||
import { Avatar, Button, Col, Container, Divider, Link, Modal, Row, Text } from '@nextui-org/react'
|
||||
import { Fragment } from 'react'
|
||||
|
||||
@ -29,7 +28,7 @@ export default function SessionSignModal() {
|
||||
// Handle approve action (logic varies based on request method)
|
||||
async function onApprove() {
|
||||
if (requestEvent) {
|
||||
const response = await approveEIP155Request(requestEvent.request, wallet)
|
||||
const response = await approveEIP155Request(requestEvent)
|
||||
await walletConnectClient.respond({
|
||||
topic: requestEvent.topic,
|
||||
response
|
||||
|
@ -3,7 +3,6 @@ import ModalStore from '@/store/ModalStore'
|
||||
import { getSignTypedDataParamsData } from '@/utils/HelperUtil'
|
||||
import { approveEIP155Request, rejectEIP155Request } from '@/utils/RequestHandlerUtil'
|
||||
import { walletConnectClient } from '@/utils/WalletConnectUtil'
|
||||
import { wallet } from '@/utils/WalletUtil'
|
||||
import { Avatar, Button, Col, Container, Divider, Link, Modal, Row, Text } from '@nextui-org/react'
|
||||
import { Fragment } from 'react'
|
||||
import { CodeBlock, codepen } from 'react-code-blocks'
|
||||
@ -30,7 +29,7 @@ export default function SessionSignTypedDataModal() {
|
||||
// Handle approve action (logic varies based on request method)
|
||||
async function onApprove() {
|
||||
if (requestEvent) {
|
||||
const response = await approveEIP155Request(requestEvent.request, wallet)
|
||||
const response = await approveEIP155Request(requestEvent)
|
||||
await walletConnectClient.respond({
|
||||
topic: requestEvent.topic,
|
||||
response
|
||||
|
Loading…
Reference in New Issue
Block a user