feat: UI update (#295)

* chore: removes web3wallet example

* refactor: updates sign-client to web3wallet

* feat: implements auth

* fix: resolves bug preventing approving only optional namespaces

* feat: implements new design * UX flow for approving session proposals

* feat: implements new designs in all modals

* refactor: removes unused imports & groups imports into remote & local

* feat: implements `isScam` alert on main request modal

* feat: implements `threat prompt`

* feat: updates all modals to new designs

* chore: retrigger deployment

---------

Co-authored-by: Gancho Radkov <ganchoradkov@gmail.com>
This commit is contained in:
Gancho Radkov 2023-09-26 15:01:41 +03:00 committed by GitHub
parent 65162f2d57
commit 371e4f334d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1960 additions and 906 deletions

View File

@ -12,9 +12,15 @@
"@cosmjs/amino": "0.31.1",
"@cosmjs/encoding": "0.31.1",
"@cosmjs/proto-signing": "0.31.1",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@json-rpc-tools/utils": "1.7.6",
"@kadena/cryptography-utils": "^0.3.2",
"@kadena/types": "^0.3.2",
"@material-ui/core": "^4.12.4",
"@material-ui/icons": "^4.11.3",
"@mui/icons-material": "^5.14.9",
"@mui/material": "^5.14.10",
"@multiversx/sdk-core": "12.8.0",
"@multiversx/sdk-wallet": "4.2.0",
"@near-wallet-selector/wallet-utils": "^7.0.2",

View File

@ -0,0 +1,14 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="icon_verified-domain" clip-path="url(#clip0_1779_14845)">
<path id="Vector" d="M7.66667 13.3333C6.54591 13.3333 5.45032 13.001 4.51844 12.3783C3.58656 11.7557 2.86025 10.8707 2.43135 9.83521C2.00246 8.79976 1.89024 7.66038 2.10889 6.56116C2.32754 5.46193 2.86723 4.45223 3.65973 3.65973C4.45223 2.86723 5.46193 2.32754 6.56116 2.10889C7.66038 1.89024 8.79976 2.00246 9.83521 2.43135C10.8707 2.86025 11.7557 3.58656 12.3783 4.51844C13.001 5.45032 13.3333 6.54591 13.3333 7.66667" stroke="#1F81E2" style="stroke:#1F81E2;stroke:color(display-p3 0.1216 0.5059 0.8863);stroke-opacity:1;" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector_2" d="M2.36328 5.66699H12.9694" stroke="#1F81E2" style="stroke:#1F81E2;stroke:color(display-p3 0.1216 0.5059 0.8863);stroke-opacity:1;" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector_3" d="M2.36328 9.66699H7.66633" stroke="#1F81E2" style="stroke:#1F81E2;stroke:color(display-p3 0.1216 0.5059 0.8863);stroke-opacity:1;" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector_4" d="M7.66699 13.3333C6.28628 13.3333 5.16699 10.7963 5.16699 7.66667C5.16699 4.53705 6.28628 2 7.66699 2C9.04771 2 10.167 4.53705 10.167 7.66667C10.167 8.62974 10.0957 9.33043 10.0463 9.70046" stroke="#1F81E2" style="stroke:#1F81E2;stroke:color(display-p3 0.1216 0.5059 0.8863);stroke-opacity:1;" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector_5" d="M14.5 10.5L12.0557 13.3333L10.7217 12.0003" stroke="#1F81E2" style="stroke:#1F81E2;stroke:color(display-p3 0.1216 0.5059 0.8863);stroke-opacity:1;" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_1779_14845">
<rect width="16" height="16" fill="white" style="fill:white;fill:white;fill-opacity:1;"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,29 @@
import { cosmosAddresses } from '@/utils/CosmosWalletUtil'
import { eip155Addresses } from '@/utils/EIP155WalletUtil'
import { polkadotAddresses } from '@/utils/PolkadotWalletUtil'
import { multiversxAddresses } from '@/utils/MultiversxWalletUtil'
import { tronAddresses } from '@/utils/TronWalletUtil'
import { tezosAddresses } from '@/utils/TezosWalletUtil'
import { solanaAddresses } from '@/utils/SolanaWalletUtil'
import { nearAddresses } from '@/utils/NearWalletUtil'
import { kadenaAddresses } from '@/utils/KadenaWalletUtil'
import { useMemo } from 'react'
import { Row } from '@nextui-org/react'
import { getChainData } from '@/data/chainsUtil'
interface Props {
address?: string
}
export default function ChainAddressMini({ address }: Props) {
if (!address) return <></>
return (
<>
<Row>
<span style={{ marginLeft: '5px' }}>
{address.substring(0, 6)}...{address.substring(address.length - 6)}
</span>
</Row>
</>
)
}

View File

@ -0,0 +1,33 @@
import { COSMOS_MAINNET_CHAINS } from '@/data/COSMOSData'
import { EIP155_CHAINS } from '@/data/EIP155Data'
import { KADENA_CHAINS } from '@/data/KadenaData'
import { MULTIVERSX_CHAINS } from '@/data/MultiversxData'
import { NEAR_CHAINS } from '@/data/NEARData'
import { POLKADOT_CHAINS } from '@/data/PolkadotData'
import { SOLANA_CHAINS } from '@/data/SolanaData'
import { TEZOS_CHAINS } from '@/data/TezosData'
import { TRON_CHAINS } from '@/data/TronData'
import { getChainData } from '@/data/chainsUtil'
import { Card, Row, styled, Image, Avatar } from '@nextui-org/react'
import { ReactNode, useMemo } from 'react'
interface Props {
chainId?: string | number
}
// const StyledLogo = styled(Image, {})
export default function ChainDataMini({ chainId }: Props) {
const chainData = useMemo(() => getChainData(chainId!), [chainId])
console.log(chainData)
if (!chainData) return <></>
return (
<>
<Row>
<Avatar size={'xs'} src={chainData.logo} />
<span style={{ marginLeft: '5px' }}>{chainData.name}</span>
</Row>
</>
)
}

View File

@ -0,0 +1,65 @@
import SettingsStore from '@/store/SettingsStore'
import { Button, Modal, Row } from '@nextui-org/react'
import { useMemo } from 'react'
import { useSnapshot } from 'valtio'
interface Props {
onApprove: () => void
onReject: () => void
infoBoxCondition?: boolean
infoBoxText?: string
disabledApprove?: boolean
}
export default function ModalFooter({
onApprove,
onReject,
infoBoxCondition,
infoBoxText,
disabledApprove
}: Props) {
const { currentRequestVerifyContext } = useSnapshot(SettingsStore.state)
const validation = currentRequestVerifyContext?.verified.validation
const approveButtonColor: any = useMemo(() => {
switch (validation) {
case 'INVALID':
return 'error'
case 'UNKNOWN':
return 'warning'
default:
return 'success'
}
}, [validation])
return (
<Modal.Footer>
{infoBoxCondition && (
<Row style={{ textAlign: 'initial' }}>
<span>{infoBoxText || ''}</span>
</Row>
)}
<Row justify="space-between">
<Button
auto
flat
style={{ color: 'white', backgroundColor: 'grey' }}
onPress={onReject}
data-testid="session-reject-button"
>
Reject
</Button>
<Button
auto
flat
color={approveButtonColor}
disabled={disabledApprove}
onPress={onApprove}
data-testid="session-approve-button"
>
Approve
</Button>
</Row>
</Modal.Footer>
)
}

View File

@ -1,9 +1,10 @@
import { useMemo } from 'react'
import { useSnapshot } from 'valtio'
import SettingsStore from '@/store/SettingsStore'
import { getVerifyStatus } from '@/utils/HelperUtil'
import { Avatar, Col, Link, Row, Text } from '@nextui-org/react'
import ReportIcon from '@mui/icons-material/Report'
import ReportProblemIcon from '@mui/icons-material/ReportProblem'
import NewReleasesIcon from '@mui/icons-material/NewReleases'
import { Avatar, Col, Link, Row, Text, styled } from '@nextui-org/react'
import { SignClientTypes } from '@walletconnect/types'
/**
@ -11,32 +12,95 @@ import { SignClientTypes } from '@walletconnect/types'
*/
interface IProps {
metadata: SignClientTypes.Metadata
intention?: string
}
const StyledLink = styled('span', {
color: '#697177'
} as any)
const StyledVerifiedIcon = styled('img', {
verticalAlign: 'middle',
marginRight: '5px'
} as any)
const StyledUnknownRow = styled(Row, {
color: '$warning'
// marginTop: '10px'
} as any)
const StyledUnknownContainer = styled('div', {
padding: '7px'
} as any)
const StyledInvalidRow = styled(Row, {
color: '$error'
// marginTop: '10px'
} as any)
const StyledInvalidContainer = styled('div', {
padding: '7px'
} as any)
/**
* Components
*/
export default function ProjectInfoCard({ metadata }: IProps) {
export default function ProjectInfoCard({ metadata, intention }: IProps) {
const { currentRequestVerifyContext } = useSnapshot(SettingsStore.state)
const validation = currentRequestVerifyContext?.verified.validation
const { icons, name, url } = metadata
const validation = useMemo(
() => getVerifyStatus(currentRequestVerifyContext),
[currentRequestVerifyContext]
)
return (
<>
<Row align="center">
<Col span={3}>
<Avatar src={icons[0]} />
</Col>
<Col span={15}>
<Text h5 data-testid="session-info-card-text">{name}</Text>
<div style={{ textAlign: 'center' }}>
<Row>
<Link href={url} data-testid="session-info-card-url">{url}</Link>
<Text style={{ marginLeft: '5px' }} data-testid="session-info-card-verify">{validation}</Text>
</Row>
<Col>
<Avatar style={{ margin: 'auto' }} src={icons[0]} size={'xl'} />
</Col>
</Row>
</>
<Row align="center">
<Col>
<Text h3 data-testid="session-info-card-text">
<span>{name}</span> <br />
<Text h4> wants to {intention ? intention : 'connect'}</Text>
</Text>
</Col>
</Row>
<Row align="center">
<Col>
{validation == 'VALID' ? <StyledVerifiedIcon src="/icons/verified-domain.svg" /> : null}
<Link style={{ verticalAlign: 'middle' }} href={url} data-testid="session-info-card-url">
<StyledLink>{url}</StyledLink>
</Link>
</Col>
</Row>
{currentRequestVerifyContext?.verified.isScam ? (
<StyledInvalidRow>
<Col style={{ margin: 'auto' }}>
<StyledInvalidContainer>
<NewReleasesIcon style={{ verticalAlign: 'bottom' }} />
Potential threat
</StyledInvalidContainer>
</Col>
</StyledInvalidRow>
) : validation == 'UNKNOWN' ? (
<StyledUnknownRow>
<Col style={{ margin: 'auto' }}>
<StyledUnknownContainer>
<ReportIcon style={{ verticalAlign: 'bottom' }} />
Cannot Verify
</StyledUnknownContainer>
</Col>
</StyledUnknownRow>
) : validation == 'INVALID' ? (
<StyledInvalidRow>
<Col style={{ margin: 'auto' }}>
<StyledInvalidContainer>
<ReportProblemIcon style={{ verticalAlign: 'bottom', marginRight: '2px' }} />
Invalid Domain
</StyledInvalidContainer>
</Col>
</StyledInvalidRow>
) : null}
</div>
)
}

View File

@ -5,7 +5,7 @@ import { Fragment, ReactNode } from 'react'
* Types
*/
interface IProps {
title: string
title?: string
children: ReactNode | ReactNode[]
}
@ -15,10 +15,11 @@ interface IProps {
export default function RequestModalContainer({ children, title }: IProps) {
return (
<Fragment>
{title ? (
<Modal.Header>
<Text h3>{title}</Text>
</Modal.Header>
) : null}
<Modal.Body>
<Container css={{ padding: 0 }}>{children}</Container>
</Modal.Body>

View File

@ -0,0 +1,113 @@
import { useMemo } from 'react'
import { useSnapshot } from 'valtio'
import { Image, StyledText } from '@nextui-org/react'
import SettingsStore from '@/store/SettingsStore'
import ReportIcon from '@mui/icons-material/Report'
import ReportProblemIcon from '@mui/icons-material/ReportProblem'
import NewReleasesIcon from '@mui/icons-material/NewReleases'
import { Avatar, Col, Link, Row, Text, styled } from '@nextui-org/react'
import { SignClientTypes } from '@walletconnect/types'
/**
* Types
*/
interface IProps {
metadata: SignClientTypes.Metadata
}
const StyledContainer = styled(Row, {
padding: '7px',
borderRadius: '30px',
marginTop: '10px',
marginBottom: '10px'
} as any)
const StyledUnknownRow = styled(StyledContainer, {
color: '$warning',
border: '0.5px solid $warning'
} as any)
const StyledUnknownContainer = styled('div', {
textAlign: 'initial'
} as any)
const StyledInvalidRow = styled(StyledContainer, {
color: '$error',
border: '0.5px solid $error'
} as any)
const StyledInvalidContainer = styled('div', {
textAlign: 'initial'
} as any)
const StyledDescription = styled(Text, {
lineHeight: '20px',
fontSize: '15px'
} as any)
/**
* Components
*/
export default function VerifyInfobox({ metadata }: IProps) {
const { currentRequestVerifyContext } = useSnapshot(SettingsStore.state)
const validation = currentRequestVerifyContext?.verified.validation
return (
<div style={{ textAlign: 'center' }}>
{currentRequestVerifyContext?.verified.isScam ? (
<StyledInvalidRow>
<Col style={{ margin: 'auto' }} span={2}>
<NewReleasesIcon style={{ verticalAlign: 'bottom' }} />
</Col>
<Col style={{ margin: 'auto' }}>
<Row>Known secury risk</Row>
<Row>
<StyledInvalidContainer>
<StyledDescription>
This website is flagged as unsafe by multiple security reports. Leave immediately
to protect your assets.
</StyledDescription>
</StyledInvalidContainer>
</Row>
</Col>
</StyledInvalidRow>
) : validation == 'UNKNOWN' ? (
<StyledUnknownRow>
<Col style={{ margin: 'auto' }} span={2}>
<ReportIcon style={{ verticalAlign: 'bottom' }} />
</Col>
<Col style={{ margin: 'auto' }}>
<Row>
<StyledUnknownContainer>Unknown domain</StyledUnknownContainer>
</Row>
<Row>
<StyledUnknownContainer>
<StyledDescription>
This domain cannot be verified. Please check the request carefully before
approving.
</StyledDescription>
</StyledUnknownContainer>
</Row>
</Col>
</StyledUnknownRow>
) : validation == 'INVALID' ? (
<StyledInvalidRow>
<Col style={{ margin: 'auto' }} span={2}>
<ReportProblemIcon style={{ verticalAlign: 'bottom' }} />
</Col>
<Col style={{ margin: 'auto' }}>
<Row>
<>Domain mismatch</>
</Row>
<Row>
<StyledInvalidContainer>
<StyledDescription>
This website has a domain that does not match the sender of this request.
Approving may lead to loss of funds.
</StyledDescription>
</StyledInvalidContainer>
</Row>
</Col>
</StyledInvalidRow>
) : null}
</div>
)
}

View File

@ -12,7 +12,8 @@ export const COSMOS_MAINNET_CHAINS = {
name: 'Cosmos Hub',
logo: '/chain-logos/cosmos-cosmoshub-4.png',
rgb: '107, 111, 147',
rpc: ''
rpc: '',
namespace: 'cosmos'
}
}

View File

@ -17,35 +17,40 @@ export const EIP155_MAINNET_CHAINS = {
name: 'Ethereum',
logo: '/chain-logos/eip155-1.png',
rgb: '99, 125, 234',
rpc: 'https://cloudflare-eth.com/'
rpc: 'https://cloudflare-eth.com/',
namespace: 'eip155'
},
'eip155:43114': {
chainId: 43114,
name: 'Avalanche C-Chain',
logo: '/chain-logos/eip155-43113.png',
rgb: '232, 65, 66',
rpc: 'https://api.avax.network/ext/bc/C/rpc'
rpc: 'https://api.avax.network/ext/bc/C/rpc',
namespace: 'eip155'
},
'eip155:137': {
chainId: 137,
name: 'Polygon',
logo: '/chain-logos/eip155-137.png',
rgb: '130, 71, 229',
rpc: 'https://polygon-rpc.com/'
rpc: 'https://polygon-rpc.com/',
namespace: 'eip155'
},
'eip155:10': {
chainId: 10,
name: 'Optimism',
logo: '/chain-logos/eip155-10.png',
rgb: '235, 0, 25',
rpc: 'https://mainnet.optimism.io'
rpc: 'https://mainnet.optimism.io',
namespace: 'eip155'
},
'eip155:324': {
chainId: 324,
name: 'zkSync Era',
logo: '/chain-logos/eip155-324.svg',
rgb: '242, 242, 242',
rpc: 'https://mainnet.era.zksync.io/'
rpc: 'https://mainnet.era.zksync.io/',
namespace: 'eip155'
}
}
@ -55,35 +60,40 @@ export const EIP155_TEST_CHAINS = {
name: 'Ethereum Goerli',
logo: '/chain-logos/eip155-1.png',
rgb: '99, 125, 234',
rpc: 'https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161'
rpc: 'https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161',
namespace: 'eip155'
},
'eip155:43113': {
chainId: 43113,
name: 'Avalanche Fuji',
logo: '/chain-logos/eip155-43113.png',
rgb: '232, 65, 66',
rpc: 'https://api.avax-test.network/ext/bc/C/rpc'
rpc: 'https://api.avax-test.network/ext/bc/C/rpc',
namespace: 'eip155'
},
'eip155:80001': {
chainId: 80001,
name: 'Polygon Mumbai',
logo: '/chain-logos/eip155-137.png',
rgb: '130, 71, 229',
rpc: 'https://matic-mumbai.chainstacklabs.com'
rpc: 'https://matic-mumbai.chainstacklabs.com',
namespace: 'eip155'
},
'eip155:420': {
chainId: 420,
name: 'Optimism Goerli',
logo: '/chain-logos/eip155-10.png',
rgb: '235, 0, 25',
rpc: 'https://goerli.optimism.io'
rpc: 'https://goerli.optimism.io',
namespace: 'eip155'
},
'eip155:280': {
chainId: 280,
name: 'zkSync Era Testnet',
logo: '/chain-logos/eip155-324.svg',
rgb: '242, 242, 242',
rpc: 'https://testnet.era.zksync.dev/'
rpc: 'https://testnet.era.zksync.dev/',
namespace: 'eip155'
}
}

View File

@ -11,7 +11,8 @@ export const KADENA_MAINNET_CHAINS = {
chainId: 'mainnet01',
name: 'Kadena',
logo: '/chain-logos/kadena.png',
rgb: '237, 9, 143'
rgb: '237, 9, 143',
namespace: 'kadena'
}
}
@ -20,7 +21,8 @@ export const KADENA_TEST_CHAINS = {
chainId: 'testnet04',
name: 'Kadena Testnet',
logo: '/chain-logos/kadena.png',
rgb: '237, 9, 143'
rgb: '237, 9, 143',
namespace: 'kadena'
}
}

View File

@ -12,7 +12,8 @@ export const MULTIVERSX_MAINNET_CHAINS = {
name: 'MultiversX',
logo: '/chain-logos/multiversx-1.svg',
rgb: '43, 45, 46',
rpc: ''
rpc: '',
namespace: 'mutiversx'
}
}
@ -22,7 +23,8 @@ export const MULTIVERSX_TEST_CHAINS = {
name: 'MultiversX Devnet',
logo: '/chain-logos/multiversx-1.svg',
rgb: '43, 45, 46',
rpc: ''
rpc: '',
namespace: 'mutiversx'
}
// Keep only one Test Chain visible
// 'mvx:T': {

View File

@ -25,6 +25,7 @@ type ChainMetadata = {
logo: string
rgb: string
rpc: string
namespace: string
}
export const NEAR_TEST_CHAINS: NearTestChains = {
@ -33,7 +34,8 @@ export const NEAR_TEST_CHAINS: NearTestChains = {
name: 'NEAR Testnet',
logo: '/chain-logos/near.png',
rgb: '99, 125, 234',
rpc: 'https://rpc.testnet.near.org'
rpc: 'https://rpc.testnet.near.org',
namespace: 'near'
}
}

View File

@ -12,7 +12,8 @@ export const POLKADOT_MAINNET_CHAINS = {
name: 'Polkadot',
logo: '/chain-logos/polkadot.svg',
rgb: '230, 1, 122',
rpc: ''
rpc: '',
namespace: 'polkadot'
}
}
@ -22,7 +23,8 @@ export const POLKADOT_TEST_CHAINS = {
name: 'Polkadot Westend',
logo: '/chain-logos/westend.svg',
rgb: '218, 104, 167',
rpc: ''
rpc: '',
namespace: 'polkadot'
}
}

View File

@ -12,7 +12,8 @@ export const SOLANA_MAINNET_CHAINS = {
name: 'Solana',
logo: '/chain-logos/solana-4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ.png',
rgb: '30, 240, 166',
rpc: ''
rpc: '',
namespace: 'solana'
}
}
@ -22,7 +23,8 @@ export const SOLANA_TEST_CHAINS = {
name: 'Solana Devnet',
logo: '/chain-logos/solana-4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ.png',
rgb: '30, 240, 166',
rpc: ''
rpc: '',
namespace: 'solana'
}
}

View File

@ -4,6 +4,7 @@ type ChainMetadata = {
logo: string
rgb: string
rpc: string
namespace: string
}
/**
@ -20,7 +21,8 @@ export const TEZOS_MAINNET_CHAINS: Record<string, ChainMetadata> = {
name: 'Tezos',
logo: '/chain-logos/tezos.svg',
rgb: '44, 125, 247',
rpc: 'https://mainnet.api.tez.ie'
rpc: 'https://mainnet.api.tez.ie',
namespace: 'tezos'
}
}
@ -30,7 +32,8 @@ export const TEZOS_TEST_CHAINS: Record<string, ChainMetadata> = {
name: 'Tezos Testnet',
logo: '/chain-logos/tezos.svg',
rgb: '44, 125, 247',
rpc: 'https://ghostnet.ecadinfra.com'
rpc: 'https://ghostnet.ecadinfra.com',
namespace: 'tezos'
}
}

View File

@ -13,6 +13,7 @@ type ChainMetadata = {
logo: string
rgb: string
fullNode: string
namespace: string
}
/**
@ -24,7 +25,8 @@ export const TRON_MAINNET_CHAINS: TRONChains = {
name: 'Tron',
logo: 'https://tronscan.io/static/media/TRON.4a760cebd163969b2ee874abf2415e9a.svg',
rgb: '183, 62, 49',
fullNode: 'https://api.trongrid.io'
fullNode: 'https://api.trongrid.io',
namespace: 'tron'
}
}
@ -34,7 +36,8 @@ export const TRON_TEST_CHAINS: TRONChains = {
name: 'Tron Testnet',
logo: 'https://tronscan.io/static/media/TRON.4a760cebd163969b2ee874abf2415e9a.svg',
rgb: '183, 62, 49',
fullNode: 'https://nile.trongrid.io/'
fullNode: 'https://nile.trongrid.io/',
namespace: 'tron'
}
}

View File

@ -0,0 +1,27 @@
import { COSMOS_MAINNET_CHAINS } from './COSMOSData'
import { EIP155_CHAINS } from './EIP155Data'
import { KADENA_CHAINS } from './KadenaData'
import { MULTIVERSX_CHAINS } from './MultiversxData'
import { NEAR_CHAINS } from './NEARData'
import { POLKADOT_CHAINS } from './PolkadotData'
import { SOLANA_CHAINS } from './SolanaData'
import { TEZOS_CHAINS } from './TezosData'
import { TRON_CHAINS } from './TronData'
export const ALL_CHAINS = {
...EIP155_CHAINS,
...COSMOS_MAINNET_CHAINS,
...KADENA_CHAINS,
...MULTIVERSX_CHAINS,
...NEAR_CHAINS,
...POLKADOT_CHAINS,
...SOLANA_CHAINS,
...TEZOS_CHAINS,
...TRON_CHAINS
}
export function getChainData(chainId: string | number) {
if (!chainId) return
chainId = chainId.toString().includes(':') ? chainId.toString().split(':')[1] : chainId
return Object.values(ALL_CHAINS).find(chain => chain.chainId == chainId)
}

View File

@ -2,7 +2,7 @@ import toast from 'react-hot-toast'
import { COSMOS_MAINNET_CHAINS, TCosmosChain } from '@/data/COSMOSData'
import { EIP155_CHAINS, TEIP155Chain } from '@/data/EIP155Data'
import { MULTIVERSX_CHAINS, TMultiversxChain } from '@/data/MultiversxData'
import { NEAR_TEST_CHAINS, TNearChain } from '@/data/NEARData'
import { NEAR_CHAINS, NEAR_TEST_CHAINS, TNearChain } from '@/data/NEARData'
import { POLKADOT_CHAINS, TPolkadotChain } from '@/data/PolkadotData'
import { SOLANA_CHAINS, TSolanaChain } from '@/data/SolanaData'
import { TEZOS_CHAINS, TTezosChain } from '@/data/TezosData'
@ -163,18 +163,6 @@ export function formatChainName(chainId: string) {
)
}
export function getVerifyStatus(context?: Verify.Context) {
if (!context) return ''
switch (context.verified.validation) {
case 'VALID':
return '✅ Verified'
case 'INVALID':
return '❌ Origin does not match'
case 'UNKNOWN':
return '❓ Unknown'
}
}
export function styledToast(message: string, type: string) {
if (type === 'success') {
toast.success(message, {

View File

@ -1,21 +1,23 @@
import RequestModalContainer from '@/components/RequestModalContainer'
import ModalStore from '@/store/ModalStore'
import SettingsStore from '@/store/SettingsStore'
import { approveEIP155Request, rejectEIP155Request } from '@/utils/EIP155RequestHandlerUtil'
import { eip155Addresses, eip155Wallets } from '@/utils/EIP155WalletUtil'
import { getSignParamsMessage } from '@/utils/HelperUtil'
import { web3wallet } from '@/utils/WalletConnectUtil'
import { Button, Col, Divider, Modal, Row, Text, Code } from '@nextui-org/react'
import { getSdkError } from '@walletconnect/utils'
import { Fragment } from 'react'
import { useSnapshot } from 'valtio'
import { Col, Divider, Row, Text, Code } from '@nextui-org/react'
import { getSdkError } from '@walletconnect/utils'
import ModalFooter from '@/components/ModalFooter'
import ProjectInfoCard from '@/components/ProjectInfoCard'
import RequestModalContainer from '@/components/RequestModalContainer'
import VerifyInfobox from '@/components/VerifyInfobox'
import ModalStore from '@/store/ModalStore'
import SettingsStore from '@/store/SettingsStore'
import { eip155Addresses, eip155Wallets } from '@/utils/EIP155WalletUtil'
import { web3wallet } from '@/utils/WalletConnectUtil'
import RequestModal from './RequestModal'
export default function AuthRequestModal() {
const { account } = useSnapshot(SettingsStore.state)
console.log('modal data', ModalStore.state.data, account)
// Get request and wallet data from store
const request = ModalStore.state.data?.request
// Ensure request and wallet are defined
if (!request) {
return <Text>Missing request data</Text>
@ -61,9 +63,12 @@ export default function AuthRequestModal() {
}
}
return (
<Fragment>
<RequestModalContainer title="Auth Message">
<Divider y={2} />
<RequestModal
intention="request a signature"
metadata={request.params.requester.metadata}
onApprove={onApprove}
onReject={onReject}
>
<Row>
<Col>
<Text h5>Message</Text>
@ -72,17 +77,6 @@ export default function AuthRequestModal() {
</Code>
</Col>
</Row>
<Divider y={2} />
</RequestModalContainer>
<Modal.Footer>
<Button auto flat color="error" onClick={onReject}>
Reject
</Button>
<Button auto flat color="success" onClick={onApprove}>
Approve
</Button>
</Modal.Footer>
</Fragment>
</RequestModal>
)
}

View File

@ -0,0 +1,77 @@
import { Fragment, ReactNode, useMemo, useState } from 'react'
import { Divider, Text } from '@nextui-org/react'
import { CoreTypes } from '@walletconnect/types'
import ModalFooter from '@/components/ModalFooter'
import ProjectInfoCard from '@/components/ProjectInfoCard'
import RequestModalContainer from '@/components/RequestModalContainer'
import VerifyInfobox from '@/components/VerifyInfobox'
import { useSnapshot } from 'valtio'
import SettingsStore from '@/store/SettingsStore'
import ThreatPrompt from './TheatPrompt'
interface IProps {
children: ReactNode
metadata: CoreTypes.Metadata
onApprove: () => void
onReject: () => void
intention?: string
infoBoxCondition?: boolean
infoBoxText?: string
disabledApprove?: boolean
}
export default function RequestModal({
children,
metadata,
onApprove,
onReject,
intention,
infoBoxCondition,
infoBoxText,
disabledApprove
}: IProps) {
const { currentRequestVerifyContext } = useSnapshot(SettingsStore.state)
const isScam = currentRequestVerifyContext?.verified.isScam
const [threatAcknowledged, setThreatAcknowledged] = useState(false)
const threatPromptContent = useMemo(() => {
return (
<ThreatPrompt
metadata={metadata}
onApprove={() => setThreatAcknowledged(true)}
onReject={onReject}
/>
)
}, [metadata, onReject])
const modalContent = useMemo(() => {
return (
<>
<RequestModalContainer title="">
<ProjectInfoCard metadata={metadata} intention={intention} />
<Divider y={1} />
{children}
<Divider y={1} />
<VerifyInfobox metadata={metadata} />
</RequestModalContainer>
<ModalFooter
onApprove={onApprove}
onReject={onReject}
infoBoxCondition={infoBoxCondition}
infoBoxText={infoBoxText}
disabledApprove={disabledApprove}
/>
</>
)
}, [
children,
disabledApprove,
infoBoxCondition,
infoBoxText,
intention,
metadata,
onApprove,
onReject
])
return <Fragment>{isScam && !threatAcknowledged ? threatPromptContent : modalContent}</Fragment>
}

View File

@ -1,7 +1,12 @@
import { Col, Divider, Grid, Row, Text, styled } from '@nextui-org/react'
import { Fragment, useCallback, useMemo } from 'react'
import { buildApprovedNamespaces, getSdkError } from '@walletconnect/utils'
import DoneIcon from '@mui/icons-material/Done'
import CloseIcon from '@mui/icons-material/Close'
import ProjectInfoCard from '@/components/ProjectInfoCard'
import ProposalSelectSection from '@/components/ProposalSelectSection'
import RequestModalContainer from '@/components/RequestModalContainer'
import SessionProposalChainCard from '@/components/SessionProposalChainCard'
import ModalStore from '@/store/ModalStore'
import { cosmosAddresses } from '@/utils/CosmosWalletUtil'
import { eip155Addresses } from '@/utils/EIP155WalletUtil'
@ -9,33 +14,215 @@ import { polkadotAddresses } from '@/utils/PolkadotWalletUtil'
import { multiversxAddresses } from '@/utils/MultiversxWalletUtil'
import { tronAddresses } from '@/utils/TronWalletUtil'
import { tezosAddresses } from '@/utils/TezosWalletUtil'
import {
isCosmosChain,
isEIP155Chain,
isSolanaChain,
isPolkadotChain,
isNearChain,
isMultiversxChain,
isTronChain,
isTezosChain,
isKadenaChain,
styledToast
} from '@/utils/HelperUtil'
import { solanaAddresses } from '@/utils/SolanaWalletUtil'
import { web3wallet } from '@/utils/WalletConnectUtil'
import { Button, Divider, Modal, Text } from '@nextui-org/react'
import { SessionTypes } from '@walletconnect/types'
import { getSdkError, mergeArrays } from '@walletconnect/utils'
import { Fragment, useEffect, useState } from 'react'
import { nearAddresses } from '@/utils/NearWalletUtil'
import { kadenaAddresses } from '@/utils/KadenaWalletUtil'
import { styledToast } from '@/utils/HelperUtil'
import { web3wallet } from '@/utils/WalletConnectUtil'
import { EIP155_CHAINS, EIP155_SIGNING_METHODS } from '@/data/EIP155Data'
import { COSMOS_MAINNET_CHAINS, COSMOS_SIGNING_METHODS } from '@/data/COSMOSData'
import { KADENA_CHAINS, KADENA_SIGNING_METHODS } from '@/data/KadenaData'
import { MULTIVERSX_CHAINS, MULTIVERSX_SIGNING_METHODS } from '@/data/MultiversxData'
import { NEAR_CHAINS, NEAR_SIGNING_METHODS } from '@/data/NEARData'
import { POLKADOT_CHAINS, POLKADOT_SIGNING_METHODS } from '@/data/PolkadotData'
import { SOLANA_CHAINS, SOLANA_SIGNING_METHODS } from '@/data/SolanaData'
import { TEZOS_CHAINS, TEZOS_SIGNING_METHODS } from '@/data/TezosData'
import { TRON_CHAINS, TRON_SIGNING_METHODS } from '@/data/TronData'
import ChainDataMini from '@/components/ChainDataMini'
import ChainAddressMini from '@/components/ChainAddressMini'
import { getChainData } from '@/data/chainsUtil'
import VerifyInfobox from '@/components/VerifyInfobox'
import ModalFooter from '@/components/ModalFooter'
import RequestModal from './RequestModal'
const StyledText = styled(Text, {
fontWeight: 400
} as any)
const StyledSpan = styled('span', {
fontWeight: 400
} as any)
export default function SessionProposalModal() {
const [selectedAccounts, setSelectedAccounts] = useState<Record<string, string[]>>({})
const hasSelected = Object.keys(selectedAccounts).length
// Get proposal data and wallet address from store
const proposal = ModalStore.state.data?.proposal
console.log('proposal', proposal)
const supportedNamespaces = useMemo(() => {
// eip155
const eip155Chains = Object.keys(EIP155_CHAINS)
const eip155Methods = Object.values(EIP155_SIGNING_METHODS)
// cosmos
const cosmosChains = Object.keys(COSMOS_MAINNET_CHAINS)
const cosmosMethods = Object.values(COSMOS_SIGNING_METHODS)
// Kadena
const kadenaChains = Object.keys(KADENA_CHAINS)
const kadenaMethods = Object.values(KADENA_SIGNING_METHODS)
// multiversx
const multiversxChains = Object.keys(MULTIVERSX_CHAINS)
const multiversxMethods = Object.values(MULTIVERSX_SIGNING_METHODS)
// near
const nearChains = Object.keys(NEAR_CHAINS)
const nearMethods = Object.values(NEAR_SIGNING_METHODS)
// polkadot
const polkadotChains = Object.keys(POLKADOT_CHAINS)
const polkadotMethods = Object.values(POLKADOT_SIGNING_METHODS)
// solana
const solanaChains = Object.keys(SOLANA_CHAINS)
const solanaMethods = Object.values(SOLANA_SIGNING_METHODS)
// tezos
const tezosChains = Object.keys(TEZOS_CHAINS)
const tezosMethods = Object.values(TEZOS_SIGNING_METHODS)
// tron
const tronChains = Object.keys(TRON_CHAINS)
const tronMethods = Object.values(TRON_SIGNING_METHODS)
return {
eip155: {
chains: eip155Chains,
methods: eip155Methods,
events: ['accountsChanged', 'chainChanged'],
accounts: eip155Chains.map(chain => `${chain}:${eip155Addresses[0]}`).flat()
},
cosmos: {
chains: cosmosChains,
methods: cosmosMethods,
events: [],
accounts: cosmosChains.map(chain => `${chain}:${cosmosAddresses[0]}`).flat()
},
kadena: {
chains: kadenaChains,
methods: kadenaMethods,
events: [],
accounts: kadenaChains.map(chain => `${chain}:${kadenaAddresses[0]}`).flat()
},
multiversx: {
chains: multiversxChains,
methods: multiversxMethods,
events: [],
accounts: multiversxChains.map(chain => `${chain}:${multiversxAddresses[0]}`).flat()
},
near: {
chains: nearChains,
methods: nearMethods,
events: [],
accounts: nearChains.map(chain => `${chain}:${nearAddresses[0]}`).flat()
},
polkadot: {
chains: polkadotChains,
methods: polkadotMethods,
events: [],
accounts: polkadotChains
.map(chain => polkadotAddresses.map(address => `${chain}:${address}`))
.flat()
},
solana: {
chains: solanaChains,
methods: solanaMethods,
events: [],
accounts: solanaChains
.map(chain => solanaAddresses.map(address => `${chain}:${address}`))
.flat()
},
tezos: {
chains: tezosChains,
methods: tezosMethods,
events: [],
accounts: tezosChains
.map(chain => tezosAddresses.map(address => `${chain}:${address}`))
.flat()
},
tron: {
chains: tronChains,
methods: tronMethods,
events: [],
accounts: tronChains.map(chain => `${chain}:${tronAddresses[0]}`)
}
}
}, [])
console.log('supportedNamespaces', supportedNamespaces, eip155Addresses)
const requestedChains = useMemo(() => {
if (!proposal) return []
const required = []
for (const [key, values] of Object.entries(proposal.params.requiredNamespaces)) {
const chains = key.includes(':') ? key : values.chains
required.push(chains)
}
const optional = []
for (const [key, values] of Object.entries(proposal.params.optionalNamespaces)) {
const chains = key.includes(':') ? key : values.chains
optional.push(chains)
}
console.log('requestedChains', [...new Set([...required.flat(), ...optional.flat()])])
return [...new Set([...required.flat(), ...optional.flat()])]
}, [proposal])
// the chains that are supported by the wallet from the proposal
const supportedChains = useMemo(
() => requestedChains.map(chain => getChainData(chain!)),
[requestedChains]
)
// get required chains that are not supported by the wallet
const notSupportedChains = useMemo(() => {
if (!proposal) return []
const required = []
for (const [key, values] of Object.entries(proposal.params.requiredNamespaces)) {
const chains = key.includes(':') ? key : values.chains
required.push(chains)
}
return required
.flat()
.filter(
chain =>
!supportedChains
.map(supportedChain => `${supportedChain?.namespace}:${supportedChain?.chainId}`)
.includes(chain!)
)
}, [proposal, supportedChains])
console.log('notSupportedChains', notSupportedChains)
const getAddress = useCallback((namespace?: string) => {
if (!namespace) return 'N/A'
switch (namespace) {
case 'eip155':
return eip155Addresses[0]
case 'cosmos':
return cosmosAddresses[0]
case 'kadena':
return kadenaAddresses[0]
case 'multiversx':
return multiversxAddresses[0]
case 'near':
return nearAddresses[0]
case 'polkadot':
return polkadotAddresses[0]
case 'solana':
return solanaAddresses[0]
case 'tezos':
return tezosAddresses[0]
case 'tron':
return tronAddresses[0]
}
}, [])
const approveButtonColor: any = useMemo(() => {
switch (proposal?.verifyContext.verified.validation) {
case 'INVALID':
return 'error'
case 'UNKNOWN':
return 'warning'
default:
return 'success'
}
}, [proposal])
// Ensure proposal is defined
if (!proposal) {
@ -45,67 +232,14 @@ export default function SessionProposalModal() {
// Get required proposal data
const { id, params } = proposal
const { proposer, requiredNamespaces, optionalNamespaces, sessionProperties, relays } = params
// Add / remove address from EIP155 selection
function onSelectAccount(chain: string, account: string) {
if (selectedAccounts[chain]?.includes(account)) {
const newSelectedAccounts = selectedAccounts[chain]?.filter(a => a !== account)
setSelectedAccounts(prev => ({
...prev,
[chain]: newSelectedAccounts
}))
} else {
const prevChainAddresses = selectedAccounts[chain] ?? []
setSelectedAccounts(prev => ({
...prev,
[chain]: [...prevChainAddresses, account]
}))
}
}
const { proposer, relays } = params
// Hanlde approve action, construct session namespace
async function onApprove() {
if (proposal) {
let namespaces: SessionTypes.Namespaces = {}
const selectedOptionalNamespaces = []
for (const [chain, account] of Object.entries(selectedAccounts)) {
if (chain.includes('optional')) {
selectedOptionalNamespaces.push(chain.split(':')[1])
}
}
Object.keys(requiredNamespaces)
.concat(selectedOptionalNamespaces)
.forEach(key => {
const accounts: string[] = []
if (requiredNamespaces[key] && requiredNamespaces[key]?.chains) {
requiredNamespaces[key].chains?.map(chain => {
selectedAccounts[`required:${key}`].map(acc => accounts.push(`${chain}:${acc}`))
})
namespaces[key] = {
accounts,
methods: requiredNamespaces[key].methods,
events: requiredNamespaces[key].events,
chains: requiredNamespaces[key].chains
}
}
if (optionalNamespaces[key] && selectedAccounts[`optional:${key}`]) {
optionalNamespaces[key].chains?.map(chain => {
selectedAccounts[`optional:${key}`].forEach(acc => {
if (!accounts.includes(`${chain}:${acc}`)) {
accounts.push(`${chain}:${acc}`)
}
})
})
namespaces[key] = {
...namespaces[key],
accounts,
methods: mergeArrays(namespaces[key]?.methods, optionalNamespaces[key].methods),
events: mergeArrays(namespaces[key]?.events, optionalNamespaces[key].events),
chains: mergeArrays(namespaces[key]?.chains, optionalNamespaces[key].chains)
}
}
const namespaces = buildApprovedNamespaces({
proposal: proposal.params,
supportedNamespaces
})
console.log('approving namespaces:', namespaces)
@ -140,158 +274,64 @@ export default function SessionProposalModal() {
ModalStore.close()
}
// Render account selection checkboxes based on chain
function renderAccountSelection(chain: string, required: boolean) {
if (isEIP155Chain(chain)) {
return (
<ProposalSelectSection
addresses={eip155Addresses}
selectedAddresses={selectedAccounts[chain]}
onSelect={onSelectAccount}
chain={chain}
isRequired={required}
/>
)
} else if (isCosmosChain(chain)) {
return (
<ProposalSelectSection
addresses={cosmosAddresses}
selectedAddresses={selectedAccounts[chain]}
onSelect={onSelectAccount}
chain={chain}
isRequired={required}
/>
)
} else if (isSolanaChain(chain)) {
return (
<ProposalSelectSection
addresses={solanaAddresses}
selectedAddresses={selectedAccounts[chain]}
onSelect={onSelectAccount}
chain={chain}
isRequired={required}
/>
)
} else if (isPolkadotChain(chain)) {
return (
<ProposalSelectSection
addresses={polkadotAddresses}
selectedAddresses={selectedAccounts[chain]}
onSelect={onSelectAccount}
chain={chain}
isRequired={required}
/>
)
} else if (isNearChain(chain)) {
return (
<ProposalSelectSection
addresses={nearAddresses}
selectedAddresses={selectedAccounts[chain]}
onSelect={onSelectAccount}
chain={chain}
isRequired={required}
/>
)
} else if (isMultiversxChain(chain)) {
return (
<ProposalSelectSection
addresses={multiversxAddresses}
selectedAddresses={selectedAccounts[chain]}
onSelect={onSelectAccount}
chain={chain}
isRequired={required}
/>
)
} else if (isTronChain(chain)) {
return (
<ProposalSelectSection
addresses={tronAddresses}
selectedAddresses={selectedAccounts[chain]}
onSelect={onSelectAccount}
chain={chain}
isRequired={required}
/>
)
} else if (isTezosChain(chain)) {
return (
<ProposalSelectSection
addresses={tezosAddresses}
selectedAddresses={selectedAccounts[chain]}
onSelect={onSelectAccount}
chain={chain}
isRequired={required}
/>
)
} else if (isKadenaChain(chain)) {
return (
<ProposalSelectSection
addresses={kadenaAddresses}
selectedAddresses={selectedAccounts[chain]}
onSelect={onSelectAccount}
chain={chain}
isRequired={required}
/>
)
}
}
return (
<Fragment>
<RequestModalContainer title="Session Proposal">
<ProjectInfoCard metadata={proposer.metadata} />
<Divider y={2} />
{Object.keys(requiredNamespaces).length != 0 ? <Text h4>Required Namespaces</Text> : null}
{Object.keys(requiredNamespaces).map(chain => {
return (
<Fragment key={chain}>
<Text css={{ marginBottom: '$5' }}>{`Review ${chain} permissions`}</Text>
<SessionProposalChainCard
requiredNamespace={requiredNamespaces[chain]}
data-testid={`session-proposal-card-req-${chain}`}
/>
{renderAccountSelection(`required:${chain}`, true)}
<Divider y={2} />
</Fragment>
)
})}
{optionalNamespaces && Object.keys(optionalNamespaces).length != 0 ? (
<Text h4>Optional Namespaces</Text>
) : null}
{optionalNamespaces &&
Object.keys(optionalNamespaces).length != 0 &&
Object.keys(optionalNamespaces).map(chain => {
return (
<Fragment key={chain}>
<Text css={{ marginBottom: '$5' }}>{`Review ${chain} permissions`}</Text>
<SessionProposalChainCard
requiredNamespace={optionalNamespaces[chain]}
data-testid={`session-proposal-card-opt-${chain}`}
/>
{renderAccountSelection(`optional:${chain}`, false)}
<Divider y={2} />
</Fragment>
)
})}
</RequestModalContainer>
<Modal.Footer>
<Button auto flat color="error" onPress={onReject} data-testid="session-reject-button">
Reject
</Button>
<Button
auto
flat
color="success"
onPress={onApprove}
disabled={!hasSelected}
css={{ opacity: hasSelected ? 1 : 0.4 }}
data-testid="session-approve-button"
<RequestModal
metadata={proposal.params.proposer.metadata}
onApprove={onApprove}
onReject={onReject}
infoBoxCondition={notSupportedChains.length > 0}
infoBoxText={`The following required chains are not supported by your wallet - ${notSupportedChains.toString()}`}
disabledApprove={notSupportedChains.length > 0}
>
Approve
</Button>
</Modal.Footer>
</Fragment>
<Row>
<Col>
<StyledText h4>Requested permissions</StyledText>
</Col>
</Row>
<Row>
<Col>
<DoneIcon style={{ verticalAlign: 'bottom' }} />{' '}
<StyledSpan>View your balance and activity</StyledSpan>
</Col>
</Row>
<Row>
<Col>
<DoneIcon style={{ verticalAlign: 'bottom' }} />{' '}
<StyledSpan>Send approval requests</StyledSpan>
</Col>
</Row>
<Row>
<Col style={{ color: 'gray' }}>
<CloseIcon style={{ verticalAlign: 'bottom' }} />
<StyledSpan>Move funds without permission</StyledSpan>
</Col>
</Row>
<Grid.Container style={{ marginBottom: '10px', marginTop: '10px' }} justify={'space-between'}>
<Grid>
<Row style={{ color: 'GrayText' }}>Accounts</Row>
{supportedChains.length &&
supportedChains.map((chain, i) => {
return (
<Row key={i}>
<ChainAddressMini key={i} address={getAddress(chain?.namespace)} />
</Row>
)
})}
</Grid>
<Grid>
<Row style={{ color: 'GrayText' }} justify="flex-end">
Chains
</Row>
{supportedChains.length &&
supportedChains.map((chain, i) => {
return (
<Row key={i}>
<ChainDataMini key={i} chainId={chain?.chainId!} />
</Row>
)
})}
</Grid>
</Grid.Container>
</RequestModal>
)
}

View File

@ -1,14 +1,18 @@
import { Fragment, useState } from 'react'
import { Divider, Modal, Text } from '@nextui-org/react'
import ModalFooter from '@/components/ModalFooter'
import ProjectInfoCard from '@/components/ProjectInfoCard'
import RequestDataCard from '@/components/RequestDataCard'
import RequesDetailsCard from '@/components/RequestDetalilsCard'
import RequestMethodCard from '@/components/RequestMethodCard'
import RequestModalContainer from '@/components/RequestModalContainer'
import VerifyInfobox from '@/components/VerifyInfobox'
import ModalStore from '@/store/ModalStore'
import { approveEIP155Request, rejectEIP155Request } from '@/utils/EIP155RequestHandlerUtil'
import { styledToast } from '@/utils/HelperUtil'
import { web3wallet } from '@/utils/WalletConnectUtil'
import { Button, Divider, Loading, Modal, Text } from '@nextui-org/react'
import { Fragment, useState } from 'react'
import RequestModal from './RequestModal'
export default function SessionSendTransactionModal() {
const [loading, setLoading] = useState(false)
@ -64,31 +68,17 @@ export default function SessionSendTransactionModal() {
}
return (
<Fragment>
<RequestModalContainer title="Send / Sign Transaction">
<ProjectInfoCard metadata={requestSession.peer.metadata} />
<Divider y={2} />
<RequestModal
intention="sign a transaction"
metadata={requestSession.peer.metadata}
onApprove={onApprove}
onReject={onReject}
>
<RequestDataCard data={transaction} />
<Divider y={2} />
<Divider y={1} />
<RequesDetailsCard chains={[chainId ?? '']} protocol={requestSession.relay.protocol} />
<Divider y={2} />
<Divider y={1} />
<RequestMethodCard methods={[request.method]} />
</RequestModalContainer>
<Modal.Footer>
<Button auto flat color="error" onClick={onReject} disabled={loading}>
Reject
</Button>
<Button auto flat color="success" onClick={onApprove} disabled={loading}>
{loading ? <Loading size="sm" color="success" /> : 'Approve'}
</Button>
</Modal.Footer>
</Fragment>
</RequestModal>
)
}

View File

@ -1,14 +1,18 @@
import { Divider, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import ModalFooter from '@/components/ModalFooter'
import ProjectInfoCard from '@/components/ProjectInfoCard'
import RequestDataCard from '@/components/RequestDataCard'
import RequesDetailsCard from '@/components/RequestDetalilsCard'
import RequestMethodCard from '@/components/RequestMethodCard'
import RequestModalContainer from '@/components/RequestModalContainer'
import VerifyInfobox from '@/components/VerifyInfobox'
import ModalStore from '@/store/ModalStore'
import { approveCosmosRequest, rejectCosmosRequest } from '@/utils/CosmosRequestHandler'
import { styledToast } from '@/utils/HelperUtil'
import { web3wallet } from '@/utils/WalletConnectUtil'
import { Button, Divider, Modal, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import RequestModal from './RequestModal'
export default function SessionSignCosmosModal() {
// Get request and wallet data from store
@ -59,31 +63,17 @@ export default function SessionSignCosmosModal() {
}
return (
<Fragment>
<RequestModalContainer title="Sign Message">
<ProjectInfoCard metadata={requestSession.peer.metadata} />
<Divider y={2} />
<RequestModal
intention="sign Cosmos transaction"
metadata={requestSession.peer.metadata}
onApprove={onApprove}
onReject={onReject}
>
<RequesDetailsCard chains={[chainId ?? '']} protocol={requestSession.relay.protocol} />
<Divider y={2} />
<Divider y={1} />
<RequestDataCard data={params} />
<Divider y={2} />
<Divider y={1} />
<RequestMethodCard methods={[request.method]} />
</RequestModalContainer>
<Modal.Footer>
<Button auto flat color="error" onClick={onReject}>
Reject
</Button>
<Button auto flat color="success" onClick={onApprove}>
Approve
</Button>
</Modal.Footer>
</Fragment>
</RequestModal>
)
}

View File

@ -1,14 +1,18 @@
import { Col, Divider, Row, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import ModalFooter from '@/components/ModalFooter'
import ProjectInfoCard from '@/components/ProjectInfoCard'
import RequestDataCard from '@/components/RequestDataCard'
import RequestDetailsCard from '@/components/RequestDetalilsCard'
import RequestMethodCard from '@/components/RequestMethodCard'
import RequestModalContainer from '@/components/RequestModalContainer'
import VerifyInfobox from '@/components/VerifyInfobox'
import ModalStore from '@/store/ModalStore'
import { convertHexToUtf8, getSignParamsMessage, styledToast } from '@/utils/HelperUtil'
import { convertHexToUtf8, styledToast } from '@/utils/HelperUtil'
import { approveKadenaRequest, rejectKadenaRequest } from '@/utils/KadenaRequestHandlerUtil'
import { web3wallet } from '@/utils/WalletConnectUtil'
import { Button, Col, Divider, Modal, Row, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import RequestModal from './RequestModal'
export default function SessionSignKadenaModal() {
// Get request and wallet data from store
@ -62,16 +66,14 @@ export default function SessionSignKadenaModal() {
}
return (
<Fragment>
<RequestModalContainer title="Sign Message">
<ProjectInfoCard metadata={requestSession.peer.metadata} />
<Divider y={2} />
<RequestModal
intention="sign a Kadena message"
metadata={requestSession.peer.metadata}
onApprove={onApprove}
onReject={onReject}
>
<RequestDetailsCard chains={[chainId ?? '']} protocol={requestSession.relay.protocol} />
<Divider y={2} />
<Divider y={1} />
{message && (
<>
<Row>
@ -80,26 +82,10 @@ export default function SessionSignKadenaModal() {
<Text color="$gray400">{message}</Text>
</Col>
</Row>
<Divider y={2} />
<Divider y={1} />
</>
)}
<RequestDataCard data={params} />
<Divider y={2} />
<RequestMethodCard methods={[request.method]} />
</RequestModalContainer>
<Modal.Footer>
<Button auto flat color="error" onClick={onReject}>
Reject
</Button>
<Button auto flat color="success" onClick={onApprove}>
Approve
</Button>
</Modal.Footer>
</Fragment>
</RequestModal>
)
}

View File

@ -1,14 +1,17 @@
import { Col, Divider, Row, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import ModalFooter from '@/components/ModalFooter'
import ProjectInfoCard from '@/components/ProjectInfoCard'
import RequesDetailsCard from '@/components/RequestDetalilsCard'
import RequestMethodCard from '@/components/RequestMethodCard'
import RequestModalContainer from '@/components/RequestModalContainer'
import VerifyInfobox from '@/components/VerifyInfobox'
import ModalStore from '@/store/ModalStore'
import { approveEIP155Request, rejectEIP155Request } from '@/utils/EIP155RequestHandlerUtil'
import { getSignParamsMessage, styledToast } from '@/utils/HelperUtil'
import { web3wallet } from '@/utils/WalletConnectUtil'
import { Button, Col, Divider, Modal, Row, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import RequestModal from './RequestModal'
export default function SessionSignModal() {
// Get request and wallet data from store
const requestEvent = ModalStore.state.data?.requestEvent
@ -61,16 +64,14 @@ export default function SessionSignModal() {
}
return (
<Fragment>
<RequestModalContainer title="Sign Message">
<ProjectInfoCard metadata={requestSession.peer.metadata} />
<Divider y={2} />
<RequestModal
intention="request a signature"
metadata={requestSession.peer.metadata}
onApprove={onApprove}
onReject={onReject}
>
<RequesDetailsCard chains={[chainId ?? '']} protocol={requestSession.relay.protocol} />
<Divider y={2} />
<Divider y={1} />
<Row>
<Col>
<Text h5>Message</Text>
@ -79,20 +80,6 @@ export default function SessionSignModal() {
</Text>
</Col>
</Row>
<Divider y={2} />
<RequestMethodCard methods={[request.method]} />
</RequestModalContainer>
<Modal.Footer>
<Button auto flat color="error" onClick={onReject} data-testid="request-button-reject">
Reject
</Button>
<Button auto flat color="success" onClick={onApprove} data-testid="request-button-approve">
Approve
</Button>
</Modal.Footer>
</Fragment>
</RequestModal>
)
}

View File

@ -1,8 +1,13 @@
import { Divider, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import ModalFooter from '@/components/ModalFooter'
import ProjectInfoCard from '@/components/ProjectInfoCard'
import RequestDataCard from '@/components/RequestDataCard'
import RequesDetailsCard from '@/components/RequestDetalilsCard'
import RequestMethodCard from '@/components/RequestMethodCard'
import RequestModalContainer from '@/components/RequestModalContainer'
import VerifyInfobox from '@/components/VerifyInfobox'
import ModalStore from '@/store/ModalStore'
import { styledToast } from '@/utils/HelperUtil'
import {
@ -10,8 +15,7 @@ import {
rejectMultiversxRequest
} from '@/utils/MultiversxRequestHandlerUtil'
import { web3wallet } from '@/utils/WalletConnectUtil'
import { Button, Divider, Modal, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import RequestModal from './RequestModal'
export default function SessionSignMultiversxModal() {
// Get request and wallet data from store
@ -62,31 +66,17 @@ export default function SessionSignMultiversxModal() {
}
return (
<Fragment>
<RequestModalContainer title="Sign Message">
<ProjectInfoCard metadata={requestSession.peer.metadata} />
<Divider y={2} />
<RequestModal
intention="sign a Mtvx message"
metadata={requestSession.peer.metadata}
onApprove={onApprove}
onReject={onReject}
>
<RequesDetailsCard chains={[chainId ?? '']} protocol={requestSession.relay.protocol} />
<Divider y={2} />
<Divider y={1} />
<RequestDataCard data={params} />
<Divider y={2} />
<Divider y={1} />
<RequestMethodCard methods={[request.method]} />
</RequestModalContainer>
<Modal.Footer>
<Button auto flat color="error" onClick={onReject}>
Reject
</Button>
<Button auto flat color="success" onClick={onApprove}>
Approve
</Button>
</Modal.Footer>
</Fragment>
</RequestModal>
)
}

View File

@ -1,3 +1,7 @@
import { transactions } from 'near-api-js'
import { Divider, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import ProjectInfoCard from '@/components/ProjectInfoCard'
import RequestDataCard from '@/components/RequestDataCard'
import RequestDetailsCard from '@/components/RequestDetalilsCard'
@ -7,10 +11,10 @@ import ModalStore from '@/store/ModalStore'
import { approveNearRequest, rejectNearRequest } from '@/utils/NearRequestHandlerUtil'
import { web3wallet } from '@/utils/WalletConnectUtil'
import { NEAR_SIGNING_METHODS } from '@/data/NEARData'
import { transactions } from 'near-api-js'
import { Button, Divider, Modal, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import { styledToast } from '@/utils/HelperUtil'
import ModalFooter from '@/components/ModalFooter'
import VerifyInfobox from '@/components/VerifyInfobox'
import RequestModal from './RequestModal'
export default function SessionSignNearModal() {
// Get request and wallet data from store
@ -172,31 +176,17 @@ export default function SessionSignNearModal() {
}
return (
<Fragment>
<RequestModalContainer title="NEAR">
<ProjectInfoCard metadata={requestSession.peer.metadata} />
<Divider y={2} />
<RequestModal
intention="sign NEAR message"
metadata={requestSession.peer.metadata}
onApprove={onApprove}
onReject={onReject}
>
<RequestDetailsCard chains={[chainId ?? '']} protocol={requestSession.relay.protocol} />
<Divider y={2} />
<Divider y={1} />
<RequestDataCard data={formatParams()} />
<Divider y={2} />
<Divider y={1} />
<RequestMethodCard methods={[request.method]} />
</RequestModalContainer>
<Modal.Footer>
<Button auto flat color="error" onClick={onReject}>
Reject
</Button>
<Button auto flat color="success" onClick={onApprove}>
Approve
</Button>
</Modal.Footer>
</Fragment>
</RequestModal>
)
}

View File

@ -1,14 +1,18 @@
import { Divider, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import ModalFooter from '@/components/ModalFooter'
import ProjectInfoCard from '@/components/ProjectInfoCard'
import RequestDataCard from '@/components/RequestDataCard'
import RequesDetailsCard from '@/components/RequestDetalilsCard'
import RequestMethodCard from '@/components/RequestMethodCard'
import RequestModalContainer from '@/components/RequestModalContainer'
import VerifyInfobox from '@/components/VerifyInfobox'
import ModalStore from '@/store/ModalStore'
import { styledToast } from '@/utils/HelperUtil'
import { approvePolkadotRequest, rejectPolkadotRequest } from '@/utils/PolkadotRequestHandlerUtil'
import { web3wallet } from '@/utils/WalletConnectUtil'
import { Button, Divider, Modal, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import RequestModal from './RequestModal'
export default function SessionSignPolkadotModal() {
// Get request and wallet data from store
@ -59,31 +63,17 @@ export default function SessionSignPolkadotModal() {
}
return (
<Fragment>
<RequestModalContainer title="Sign Message">
<ProjectInfoCard metadata={requestSession.peer.metadata} />
<Divider y={2} />
<RequestModal
intention="sign a Polkadot message"
metadata={requestSession.peer.metadata}
onApprove={onApprove}
onReject={onReject}
>
<RequesDetailsCard chains={[chainId ?? '']} protocol={requestSession.relay.protocol} />
<Divider y={2} />
<Divider y={1} />
<RequestDataCard data={params} />
<Divider y={2} />
<Divider y={1} />
<RequestMethodCard methods={[request.method]} />
</RequestModalContainer>
<Modal.Footer>
<Button auto flat color="error" onClick={onReject}>
Reject
</Button>
<Button auto flat color="success" onClick={onApprove}>
Approve
</Button>
</Modal.Footer>
</Fragment>
</RequestModal>
)
}

View File

@ -1,14 +1,18 @@
import { Divider, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import ModalFooter from '@/components/ModalFooter'
import ProjectInfoCard from '@/components/ProjectInfoCard'
import RequestDataCard from '@/components/RequestDataCard'
import RequesDetailsCard from '@/components/RequestDetalilsCard'
import RequestMethodCard from '@/components/RequestMethodCard'
import RequestModalContainer from '@/components/RequestModalContainer'
import VerifyInfobox from '@/components/VerifyInfobox'
import ModalStore from '@/store/ModalStore'
import { styledToast } from '@/utils/HelperUtil'
import { approveSolanaRequest, rejectSolanaRequest } from '@/utils/SolanaRequestHandlerUtil'
import { web3wallet } from '@/utils/WalletConnectUtil'
import { Button, Divider, Modal, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import RequestModal from './RequestModal'
export default function SessionSignSolanaModal() {
// Get request and wallet data from store
@ -59,31 +63,17 @@ export default function SessionSignSolanaModal() {
}
return (
<Fragment>
<RequestModalContainer title="Sign Message">
<ProjectInfoCard metadata={requestSession.peer.metadata} />
<Divider y={2} />
<RequestModal
intention="sign a Solana message"
metadata={requestSession.peer.metadata}
onApprove={onApprove}
onReject={onReject}
>
<RequesDetailsCard chains={[chainId ?? '']} protocol={requestSession.relay.protocol} />
<Divider y={2} />
<Divider y={1} />
<RequestDataCard data={params} />
<Divider y={2} />
<Divider y={1} />
<RequestMethodCard methods={[request.method]} />
</RequestModalContainer>
<Modal.Footer>
<Button auto flat color="error" onClick={onReject}>
Reject
</Button>
<Button auto flat color="success" onClick={onApprove}>
Approve
</Button>
</Modal.Footer>
</Fragment>
</RequestModal>
)
}

View File

@ -1,14 +1,18 @@
import { Divider, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import ModalFooter from '@/components/ModalFooter'
import ProjectInfoCard from '@/components/ProjectInfoCard'
import RequestDataCard from '@/components/RequestDataCard'
import RequesDetailsCard from '@/components/RequestDetalilsCard'
import RequestMethodCard from '@/components/RequestMethodCard'
import RequestModalContainer from '@/components/RequestModalContainer'
import VerifyInfobox from '@/components/VerifyInfobox'
import ModalStore from '@/store/ModalStore'
import { styledToast } from '@/utils/HelperUtil'
import { approveTezosRequest, rejectTezosRequest } from '@/utils/TezosRequestHandlerUtil'
import { web3wallet } from '@/utils/WalletConnectUtil'
import { Button, Divider, Modal, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import RequestModal from './RequestModal'
export default function SessionSignTezosModal() {
// Get request and wallet data from store
@ -59,31 +63,17 @@ export default function SessionSignTezosModal() {
}
return (
<Fragment>
<RequestModalContainer title="Sign Message">
<ProjectInfoCard metadata={requestSession.peer.metadata} />
<Divider y={2} />
<RequestModal
intention="sign a Tezos message"
metadata={requestSession.peer.metadata}
onApprove={onApprove}
onReject={onReject}
>
<RequesDetailsCard chains={[chainId ?? '']} protocol={requestSession.relay.protocol} />
<Divider y={2} />
<Divider y={1} />
<RequestDataCard data={params} />
<Divider y={2} />
<Divider y={1} />
<RequestMethodCard methods={[request.method]} />
</RequestModalContainer>
<Modal.Footer>
<Button auto flat color="error" onClick={onReject}>
Reject
</Button>
<Button auto flat color="success" onClick={onApprove}>
Approve
</Button>
</Modal.Footer>
</Fragment>
</RequestModal>
)
}

View File

@ -1,14 +1,18 @@
import { Divider, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import ModalFooter from '@/components/ModalFooter'
import ProjectInfoCard from '@/components/ProjectInfoCard'
import RequestDataCard from '@/components/RequestDataCard'
import RequesDetailsCard from '@/components/RequestDetalilsCard'
import RequestMethodCard from '@/components/RequestMethodCard'
import RequestModalContainer from '@/components/RequestModalContainer'
import VerifyInfobox from '@/components/VerifyInfobox'
import ModalStore from '@/store/ModalStore'
import { styledToast } from '@/utils/HelperUtil'
import { approveTronRequest, rejectTronRequest } from '@/utils/TronRequestHandlerUtil'
import { web3wallet } from '@/utils/WalletConnectUtil'
import { Button, Divider, Modal, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import RequestModal from './RequestModal'
export default function SessionSignTronModal() {
// Get request and wallet data from store
@ -59,25 +63,17 @@ export default function SessionSignTronModal() {
}
return (
<Fragment>
<RequestModalContainer title="Sign Message">
<ProjectInfoCard metadata={requestSession.peer.metadata} />
<Divider y={2} />
<RequestModal
intention="sign a Tron message"
metadata={requestSession.peer.metadata}
onApprove={onApprove}
onReject={onReject}
>
<RequesDetailsCard chains={[chainId ?? '']} protocol={requestSession.relay.protocol} />
<Divider y={2} />
<Divider y={1} />
<RequestDataCard data={params} />
<Divider y={2} />
<Divider y={1} />
<RequestMethodCard methods={[request.method]} />
</RequestModalContainer>
<Modal.Footer>
<Button auto flat color="error" onClick={onReject}>
Reject
</Button>
<Button auto flat color="success" onClick={onApprove}>
Approve
</Button>
</Modal.Footer>
</Fragment>
</RequestModal>
)
}

View File

@ -1,14 +1,18 @@
import { Divider, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import ModalFooter from '@/components/ModalFooter'
import ProjectInfoCard from '@/components/ProjectInfoCard'
import RequestDataCard from '@/components/RequestDataCard'
import RequesDetailsCard from '@/components/RequestDetalilsCard'
import RequestMethodCard from '@/components/RequestMethodCard'
import RequestModalContainer from '@/components/RequestModalContainer'
import VerifyInfobox from '@/components/VerifyInfobox'
import ModalStore from '@/store/ModalStore'
import { approveEIP155Request, rejectEIP155Request } from '@/utils/EIP155RequestHandlerUtil'
import { getSignTypedDataParamsData, styledToast } from '@/utils/HelperUtil'
import { web3wallet } from '@/utils/WalletConnectUtil'
import { Button, Divider, Modal, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import RequestModal from './RequestModal'
export default function SessionSignTypedDataModal() {
// Get request and wallet data from store
@ -60,33 +64,18 @@ export default function SessionSignTypedDataModal() {
ModalStore.close()
}
}
return (
<Fragment>
<RequestModalContainer title="Sign Typed Data">
<ProjectInfoCard metadata={requestSession.peer.metadata} />
<Divider y={2} />
<RequestModal
intention="sign a message"
metadata={requestSession.peer.metadata}
onApprove={onApprove}
onReject={onReject}
>
<RequesDetailsCard chains={[chainId ?? '']} protocol={requestSession.relay.protocol} />
<Divider y={2} />
<Divider y={1} />
<RequestDataCard data={data} />
<Divider y={2} />
<Divider y={1} />
<RequestMethodCard methods={[request.method]} />
</RequestModalContainer>
<Modal.Footer>
<Button auto flat color="error" onClick={onReject} data-testid="request-button-reject">
Reject
</Button>
<Button auto flat color="success" onClick={onApprove} data-testid="request-button-approve">
Approve
</Button>
</Modal.Footer>
</Fragment>
</RequestModal>
)
}

View File

@ -1,10 +1,11 @@
import { Button, Divider, Modal, Text } from '@nextui-org/react'
import { Fragment } from 'react'
import ProjectInfoCard from '@/components/ProjectInfoCard'
import RequesDetailsCard from '@/components/RequestDetalilsCard'
import RequestMethodCard from '@/components/RequestMethodCard'
import RequestModalContainer from '@/components/RequestModalContainer'
import ModalStore from '@/store/ModalStore'
import { Button, Divider, Modal, Text } from '@nextui-org/react'
import { Fragment } from 'react'
export default function SessionUnsuportedMethodModal() {
// Get request and wallet data from store
@ -25,15 +26,14 @@ export default function SessionUnsuportedMethodModal() {
<RequestModalContainer title="Unsuported Method">
<ProjectInfoCard metadata={requestSession.peer.metadata} />
<Divider y={2} />
<Divider y={1} />
<RequesDetailsCard chains={[chainId ?? '']} protocol={requestSession.relay.protocol} />
<Divider y={2} />
<Divider y={1} />
<RequestMethodCard methods={[request.method]} />
</RequestModalContainer>
<Modal.Footer>
<Button auto flat color="error" onClick={ModalStore.close}>
Close

View File

@ -0,0 +1,84 @@
import { Fragment, ReactNode, useMemo } from 'react'
import { Col, Divider, Link, Row, Text, styled } from '@nextui-org/react'
import { CoreTypes } from '@walletconnect/types'
import NewReleasesIcon from '@mui/icons-material/NewReleases'
import ModalFooter from '@/components/ModalFooter'
import ProjectInfoCard from '@/components/ProjectInfoCard'
import RequestModalContainer from '@/components/RequestModalContainer'
import VerifyInfobox from '@/components/VerifyInfobox'
import { useSnapshot } from 'valtio'
import SettingsStore from '@/store/SettingsStore'
interface IProps {
metadata: CoreTypes.Metadata
onApprove: () => void
onReject: () => void
}
const StyledLink = styled('span', {
color: '#697177'
} as any)
const StyledProceedButton = styled('p', {
margin: '10px auto',
padding: '10px',
color: '$error',
cursor: 'pointer'
} as any)
const StyledCloseButton = styled('p', {
margin: 'auto',
padding: '10px',
border: '1px solid $error',
borderRadius: '30px'
} as any)
export default function ThreatPrompt({ metadata, onApprove, onReject }: IProps) {
const { icons, name, url } = metadata
return (
<RequestModalContainer title="">
<div style={{ textAlign: 'center', padding: '20px' }}>
<Row>
<Col>
<NewReleasesIcon sx={{ fontSize: '55px', color: '$error' }} color="error" />
</Col>
</Row>
<Row align="center">
<Col>
<Text h3>Website flagged</Text>
</Col>
</Row>
<Row align="center">
<Col>
<Link
style={{ verticalAlign: 'middle' }}
href={url}
data-testid="session-info-card-url"
>
<StyledLink>{url}</StyledLink>
</Link>
</Col>
</Row>
<div style={{ textAlign: 'center' }}>
<Divider y={1} />
<Text>
This website you`re trying to connect is flagged as malicious by multiple security
providers. Approving may lead to loss of funds.
</Text>
<Row>
<StyledProceedButton color="$error" onClick={onApprove}>
Proceed anyway
</StyledProceedButton>
</Row>
<Row>
<Col span={10} style={{ margin: 'auto', cursor: 'pointer' }} onClick={onReject}>
<StyledCloseButton>Close</StyledCloseButton>
</Col>
</Row>
</div>
</div>
</RequestModalContainer>
)
}

File diff suppressed because it is too large Load Diff