Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c56af99
Merge pull request #155 from qubic/main
sallymoc May 1, 2026
7d22a4d
fix(ui): align account names and input spacing
alez04 May 4, 2026
2efec4e
fix(onboarding): validate import passphrases earlier
alez04 May 4, 2026
b1564a1
fix(lock): keep wallets unlocked after onboarding
alez04 May 4, 2026
1c2aee0
fix(router): avoid stale unlock redirect after onboarding
alez04 May 4, 2026
7580ae9
fix: remove the new password from import vault flow
ahmed-tarek-salem May 6, 2026
89090b0
feat: removed unnecessary third step from import flow
ahmed-tarek-salem May 7, 2026
c3906b6
fix: update localization from invalid password to invalid file or pas…
ahmed-tarek-salem May 7, 2026
ac34d6d
fix: update reveal seed icon to match the web wallet
ahmed-tarek-salem May 7, 2026
c0234d7
Merge pull request #161 from qubic/fix/update-reveal-seed-icon
ahmed-tarek-salem May 7, 2026
18be40d
fix(onboarding): replace magic number with MIN_PASSPHRASE_LENGTH cons…
alez04 May 11, 2026
71c6233
fix(onboarding): remove unused locale key and passphrase trim in vaul…
alez04 May 11, 2026
d1ff019
fix(dapp): enlarge approval popup so password input fits
sallymoc May 11, 2026
b2ea753
Merge pull request #157 from qubic/fix/import-with-one-password
ahmed-tarek-salem May 11, 2026
da9e125
fix(passphrase): extract validation helper and stop trimming before v…
sallymoc May 11, 2026
a5f41c5
Merge pull request #163 from qubic/fix/dapp-approval-popup-size
sallymoc May 11, 2026
938bfec
Merge remote-tracking branch 'origin/dev' into fix/ui-ux-issues
sallymoc May 11, 2026
f76c0ee
Merge pull request #156 from qubic/fix/ui-ux-issues
sallymoc May 11, 2026
aa0edb0
fix(ui): improve input field visibility in dark mode
sallymoc May 12, 2026
e423f68
feat(ui): auto-focus single-field forms
sallymoc May 12, 2026
8cc2682
fix(onboarding): show seed validation error only after user types
sallymoc May 12, 2026
3997ab6
fix(ui): use PasswordInput for all password fields
sallymoc May 12, 2026
637e953
Merge pull request #164 from qubic/fix/password-input-dark-mode-contrast
sallymoc May 13, 2026
754d99e
Merge remote-tracking branch 'origin/dev' into fix/password-show-hide…
sallymoc May 13, 2026
4d7a0d4
Merge pull request #165 from qubic/fix/password-show-hide-toggle
sallymoc May 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@
<style>
html,
body {
width: 360px;
height: 600px;
max-height: 600px;
min-width: 360px;
min-height: 600px;
margin: 0;
overflow: hidden;
font-family: "Space Grotesk", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
Expand Down
23 changes: 17 additions & 6 deletions src/components/dapp/dapp-approval-drawer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useDrawerAutoFocus } from '@/hooks/use-drawer-auto-focus'
import { useTranslation } from 'react-i18next'
import { useLocation } from 'react-router-dom'
import { ChevronDownIcon, ChevronUpIcon, GlobeIcon, Link2OffIcon, LinkIcon } from 'lucide-react'
Expand Down Expand Up @@ -26,7 +27,11 @@ import {
import { getChromeApi } from '@/lib/dapp/chrome-api'
import { PasswordInput } from '@/components/ui/password-input'
import { formatIntegerLike, formatNumber, truncateString } from '@/lib/utils'
import { NATIVE_TOKEN_SYMBOL } from '@/lib/config/constants'
import {
DAPP_APPROVAL_QUERY_PARAM,
DAPP_APPROVAL_QUERY_VALUE,
NATIVE_TOKEN_SYMBOL,
} from '@/lib/config/constants'
import AddressLabel from '@/components/address-label'
import { useTxTypeDescription } from '@/hooks/use-tx-type-description'
import { isWalletLocked } from '@/lib/lock'
Expand All @@ -45,7 +50,8 @@ const DappApprovalDrawer = () => {
const [locked, setLocked] = useState(() => isWalletLocked())
const isDappApprovalPopup =
window.location.pathname.endsWith('popup.html') &&
new URLSearchParams(window.location.search).get('dapp') === '1'
new URLSearchParams(window.location.search).get(DAPP_APPROVAL_QUERY_PARAM) ===
DAPP_APPROVAL_QUERY_VALUE

const loadPendingRequests = useCallback(async () => {
const next = await getDappPendingRequests()
Expand Down Expand Up @@ -108,6 +114,10 @@ const DappApprovalDrawer = () => {
current.method === 'sendTransaction') &&
accountSummary?.accountWatchOnly,
)
const { ref: passphraseInputRef, onOpenAutoFocus: onPassphraseOpenAutoFocus } =
useDrawerAutoFocus<HTMLInputElement>({
enabled: requiresPassphrase && !isWatchOnlySigningRequest,
})

const title = useMemo(() => {
if (!current) return ''
Expand Down Expand Up @@ -163,13 +173,12 @@ const DappApprovalDrawer = () => {
return
}

const normalizedPassphrase = passphrase.trim()
if (approved && requiresPassphrase) {
if (!normalizedPassphrase) {
if (!passphrase.trim()) {
setError(t('passphraseAuth.validation.required'))
return
}
const validation = await validateVaultPassphrase(normalizedPassphrase)
const validation = await validateVaultPassphrase(passphrase)
if (!validation.valid) {
setError(
validation.reason === 'invalid'
Expand All @@ -194,7 +203,7 @@ const DappApprovalDrawer = () => {
payload: {
id: current.id,
approved,
passphrase: approved && requiresPassphrase ? normalizedPassphrase : undefined,
passphrase: approved && requiresPassphrase ? passphrase : undefined,
},
},
(response?: { ok?: boolean; executed?: boolean; targetTick?: number }) => {
Expand Down Expand Up @@ -246,6 +255,7 @@ const DappApprovalDrawer = () => {
}}
>
<DrawerContent
onOpenAutoFocus={onPassphraseOpenAutoFocus}
className={
isDappApprovalPopup
? 'inset-0 h-full max-h-none overflow-hidden rounded-none border-none bg-background data-[vaul-drawer-direction=bottom]:mt-0 data-[vaul-drawer-direction=bottom]:max-h-none data-[vaul-drawer-direction=bottom]:rounded-none data-[vaul-drawer-direction=bottom]:border-none'
Expand Down Expand Up @@ -387,6 +397,7 @@ const DappApprovalDrawer = () => {
)}
{requiresPassphrase && !isWatchOnlySigningRequest && (
<PasswordInput
ref={passphraseInputRef}
id="dapp-passphrase"
groupClassName="h-12"
placeholder={t('passphraseAuth.form.passphrasePlaceholder')}
Expand Down
15 changes: 15 additions & 0 deletions src/components/icons/key-vertical-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const KeyVerticalIcon = (props: React.SVGProps<SVGSVGElement>) => (
<svg
width="24"
height="24"
viewBox="0 -960 960 960"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
{...props}
>
<path d="M420-680q0-33 23.5-56.5T500-760q33 0 56.5 23.5T580-680q0 33-23.5 56.5T500-600q-33 0-56.5-23.5T420-680ZM500 0 320-180l60-80-60-80 60-85v-47q-54-32-87-86.5T260-680q0-100 70-170t170-70q100 0 170 70t70 170q0 67-33 121.5T620-472v352L500 0ZM340-680q0 56 34 98.5t86 56.5v125l-41 58 61 82-55 71 75 75 40-40v-371q52-14 86-56.5t34-98.5q0-66-47-113t-113-47q-66 0-113 47t-47 113Z" />
</svg>
)

export { KeyVerticalIcon }
40 changes: 21 additions & 19 deletions src/components/pages/manage-accounts/account-list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import {
GripVerticalIcon,
MoreHorizontalIcon,
PencilIcon,
ShieldCheckIcon,
TrashIcon,
} from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { KeyVerticalIcon } from '@/components/icons/key-vertical-icon'
import { Button } from '@/components/ui/button'
import {
DropdownMenu,
Expand Down Expand Up @@ -71,16 +71,18 @@ const AccountListItem = ({
}
}}
aria-label={`${t('accounts.manage.menu')} ${account.name}`}
className={`flex w-full items-center justify-between gap-2 rounded-lg border border-border/60 bg-card/80 px-3 py-2 ${
className={`flex w-full items-center justify-between gap-2 rounded-lg border border-border/60 bg-card/80 pl-2 pr-3 py-2 ${
isOver ? 'ring-2 ring-primary/40' : ''
} ${isDragging ? 'opacity-60' : ''}`}
>
<div className="flex min-w-0 items-center gap-2">
<div className="min-w-0">
<div className="flex min-w-0 items-center gap-2">
<span className="min-w-0 flex-1 truncate text-sm font-semibold text-foreground">
<div className="min-w-0 flex-1">
<div className="flex min-w-0 items-start justify-between gap-2">
<div className="min-w-0">
<span className="block truncate text-sm font-semibold text-foreground">
{account.name}
</span>
</div>
<div className="flex shrink-0 items-center gap-2">
{account.watchOnly && (
<span className="inline-flex shrink-0 items-center gap-1 rounded-full border border-amber-500/30 bg-amber-500/10 px-1.5 py-0.5 text-[10px] text-amber-700 dark:text-amber-200">
<EyeIcon className="h-2.5 w-2.5" />
Expand All @@ -93,18 +95,18 @@ const AccountListItem = ({
</span>
)}
</div>
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<span className="truncate">
{truncateString(account.identity, { leading: 5, trailing: 5 })}
</span>
<span className="text-[11px] font-semibold text-foreground">
{isVisible
? balance !== undefined
? formatBalanceCompact(balance)
: '--'
: HIDDEN_BALANCE}
</span>
</div>
</div>
<div className="flex items-center justify-between gap-2 text-xs text-muted-foreground">
<span className="truncate">
{truncateString(account.identity, { leading: 5, trailing: 5 })}
</span>
<span className="text-[11px] font-semibold text-foreground">
{isVisible
? balance !== undefined
? formatBalanceCompact(balance)
: '--'
: HIDDEN_BALANCE}
</span>
</div>
</div>
<div className="flex items-center gap-1">
Expand All @@ -128,7 +130,7 @@ const AccountListItem = ({
</DropdownMenuItem>
{!account.watchOnly && (
<DropdownMenuItem onClick={() => onReveal(account)}>
<ShieldCheckIcon className="h-4 w-4" />
<KeyVerticalIcon className="h-4 w-4" />
{t('accounts.manage.reveal')}
</DropdownMenuItem>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { CheckIcon } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { Button } from '@/components/ui/button'
import { useDrawerAutoFocus } from '@/hooks/use-drawer-auto-focus'
import {
Drawer,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerTitle,
} from '@/components/ui/drawer'
import { Input } from '@/components/ui/input'
import { PasswordInput } from '@/components/ui/password-input'
import { Label } from '@/components/ui/label'

type PassphrasePromptDrawerProps = {
Expand All @@ -29,18 +30,19 @@ const PassphrasePromptDrawer = ({
onSubmit,
}: PassphrasePromptDrawerProps) => {
const { t } = useTranslation()
const { ref: inputRef, onOpenAutoFocus } = useDrawerAutoFocus<HTMLInputElement>()

return (
<Drawer open={open} onOpenChange={onOpenChange}>
<DrawerContent>
<DrawerContent onOpenAutoFocus={onOpenAutoFocus}>
<DrawerHeader>
<DrawerTitle>{t('accounts.manage.passphraseTitle')}</DrawerTitle>
</DrawerHeader>
<div className="space-y-2 px-4">
<Label htmlFor="vault-passphrase">{t('accounts.manage.passphrase')}</Label>
<Input
<PasswordInput
ref={inputRef}
id="vault-passphrase"
type="password"
value={passphrase}
onChange={(event) => onPassphraseChange(event.target.value)}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { CheckIcon } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { Button } from '@/components/ui/button'
import { useDrawerAutoFocus } from '@/hooks/use-drawer-auto-focus'
import {
Drawer,
DrawerContent,
Expand Down Expand Up @@ -33,16 +34,18 @@ const RenameAccountDrawer = ({
onSubmit,
}: RenameAccountDrawerProps) => {
const { t } = useTranslation()
const { ref: inputRef, onOpenAutoFocus } = useDrawerAutoFocus<HTMLInputElement>()

return (
<Drawer open={open} onOpenChange={onOpenChange}>
<DrawerContent>
<DrawerContent onOpenAutoFocus={onOpenAutoFocus}>
<DrawerHeader>
<DrawerTitle>{t('accounts.manage.renameTitle')}</DrawerTitle>
</DrawerHeader>
<div className="space-y-2 px-4">
<Label htmlFor="rename-input">{t('accounts.manage.renameLabel')}</Label>
<Input
ref={inputRef}
id="rename-input"
value={value}
onChange={(event) => onValueChange(event.target.value)}
Expand Down
8 changes: 4 additions & 4 deletions src/components/ui/input-group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ function InputGroup({ className, ...props }: React.ComponentProps<'div'>) {
data-slot="input-group"
role="group"
className={cn(
'group/input-group border-input bg-muted/30 relative flex w-full items-center rounded-md border shadow-xs transition-[color,box-shadow] outline-none',
'h-9 min-w-0 has-[>textarea]:h-auto',
'group/input-group border-input bg-muted/50 dark:bg-white/[0.04] dark:border-white/20 relative flex w-full items-center rounded-md border shadow-xs transition-[color,box-shadow] outline-none',
'h-12 min-w-0 has-[>textarea]:h-auto',

// Variants based on alignment.
'has-[>[data-align=inline-start]]:[&>input]:pl-2',
'has-[>[data-align=inline-end]]:[&>input]:pr-2',
'has-[>[data-align=inline-start]]:[&>input]:pl-3',
'has-[>[data-align=inline-end]]:[&>input]:pr-3',
'has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3',
'has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3',

Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
type={type}
data-slot="input"
className={cn(
'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground border-input h-12 w-full min-w-0 rounded-md border bg-muted/30 px-3 py-2 text-base shadow-sm transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground border-input bg-muted/50 dark:bg-white/[0.04] dark:border-white/20 h-12 w-full min-w-0 rounded-md border px-3 py-2 text-base shadow-sm transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
className,
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function SelectTrigger({
data-slot="select-trigger"
data-size={size}
className={cn(
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive flex w-fit items-center justify-between gap-2 rounded-md border bg-muted/30 px-3 py-2 text-sm whitespace-nowrap shadow-sm transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-12 data-[size=sm]:h-10 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"border-input bg-muted/50 dark:bg-white/[0.04] dark:border-white/20 data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive flex w-fit items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm whitespace-nowrap shadow-sm transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-12 data-[size=sm]:h-10 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className,
)}
{...props}
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ function Textarea({ className, ...props }: React.ComponentProps<'textarea'>) {
<textarea
data-slot="textarea"
className={cn(
'border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive flex field-sizing-content min-h-16 w-full rounded-md border bg-muted/30 px-3 py-2 text-base shadow-sm transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
'border-input bg-muted/50 dark:bg-white/[0.04] dark:border-white/20 placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive flex field-sizing-content min-h-16 w-full rounded-md border px-3 py-2 text-base shadow-sm transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
className,
)}
{...props}
Expand Down
16 changes: 16 additions & 0 deletions src/hooks/use-drawer-auto-focus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useRef } from 'react'

type Options = { enabled?: boolean }

export const useDrawerAutoFocus = <T extends HTMLElement>(options: Options = {}) => {
const ref = useRef<T>(null)
const enabled = options.enabled ?? true

const onOpenAutoFocus = (event: Event) => {
if (!enabled) return
event.preventDefault()
ref.current?.focus()
}

return { ref, onOpenAutoFocus }
}
8 changes: 8 additions & 0 deletions src/lib/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,11 @@ export const NATIVE_TOKEN_SYMBOL = 'QU'
export const NATIVE_TOKEN_NAME = 'QUBIC'
export const EMPTY_ADDRESS = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFXIB'
export const QX_CONTRACT_INDEX = 1

export const TOOLBAR_POPUP_WIDTH_PX = 360
export const TOOLBAR_POPUP_HEIGHT_PX = 600
export const DAPP_APPROVAL_POPUP_WIDTH_PX = 420
export const DAPP_APPROVAL_POPUP_HEIGHT_PX = 720

export const DAPP_APPROVAL_QUERY_PARAM = 'dapp'
export const DAPP_APPROVAL_QUERY_VALUE = '1'
22 changes: 14 additions & 8 deletions src/lib/dapp/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,18 @@ import {
signMessageFromSeed,
signTransactionFromSeed,
} from '@/lib/dapp/signing'
import { QUBIC_RPC_BASE_URL } from '@/lib/config/constants'
import {
DAPP_APPROVAL_POPUP_HEIGHT_PX,
DAPP_APPROVAL_POPUP_WIDTH_PX,
DAPP_APPROVAL_QUERY_PARAM,
DAPP_APPROVAL_QUERY_VALUE,
QUBIC_RPC_BASE_URL,
} from '@/lib/config/constants'
import { upsertPendingTransactionInChromeStorage } from '@/lib/pending-transactions-storage'
import { normalizeBalance } from '@/lib/utils'
import { openBrowserVault, verifyVaultAccess } from '@/lib/vault'

const sdk = createSdk({ baseUrl: QUBIC_RPC_BASE_URL })
const DAPP_APPROVAL_WINDOW_WIDTH = 380
const DAPP_APPROVAL_WINDOW_HEIGHT = 600
const processingApprovalDecisionIds = new Set<string>()

type DappSendTransactionParams = {
Expand Down Expand Up @@ -120,14 +124,16 @@ const hasOpenSidePanel = async () => {
const ensureApprovalWindow = async () => {
if (await hasOpenSidePanel()) return

const popupUrl = chrome.runtime.getURL('popup.html?dapp=1')
const popupUrl = chrome.runtime.getURL(
`popup.html?${DAPP_APPROVAL_QUERY_PARAM}=${DAPP_APPROVAL_QUERY_VALUE}`,
)
try {
await chrome.windows.create({
url: popupUrl,
type: 'popup',
focused: true,
width: DAPP_APPROVAL_WINDOW_WIDTH,
height: DAPP_APPROVAL_WINDOW_HEIGHT,
width: DAPP_APPROVAL_POPUP_WIDTH_PX,
height: DAPP_APPROVAL_POPUP_HEIGHT_PX,
})
} catch {
throw new DappProviderError(
Expand Down Expand Up @@ -424,8 +430,8 @@ const executeApprovedRequest = async (
if (!decision.approved) {
return asDappFailure(request.id, 'USER_REJECTED', 'Request was rejected by user')
}
const passphrase = decision.passphrase?.trim()
if (!passphrase) {
const passphrase = decision.passphrase
if (!passphrase || !passphrase.trim()) {
return asDappFailure(request.id, 'INVALID_PASSPHRASE', 'Passphrase is required')
}
const permissions = await getDappPermissions()
Expand Down
Loading
Loading