Skip to content

Commit

Permalink
refactor: move wallet_requestPermission to interaction (#11659)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jack-Works authored May 31, 2024
1 parent f08a029 commit 87a62d4
Show file tree
Hide file tree
Showing 20 changed files with 189 additions and 153 deletions.
62 changes: 3 additions & 59 deletions packages/mask/background/services/wallet/services/connect.ts
Original file line number Diff line number Diff line change
@@ -1,65 +1,16 @@
import {
type EIP2255PermissionRequest,
type EIP2255Permission,
type EIP2255RequestedPermission,
type MaskEthereumProviderRpcError,
err,
} from '@masknet/sdk'
import { type EIP2255Permission } from '@masknet/sdk'
import { walletDatabase } from '../database/Plugin.db.js'
import { produce, enableMapSet } from 'immer'
import { ChainId } from '@masknet/web3-shared-evm'
import { openPopupWindow } from '../../helper/popup-opener.js'
import { PopupRoutes } from '@masknet/shared-base'
import { defer, type DeferTuple } from '@masknet/kit'
import type { WalletGrantedPermission } from '../database/types.js'
import { omit } from 'lodash-es'
import { Err, Ok, type Result } from 'ts-results-es'

// https://eips.ethereum.org/EIPS/eip-2255
export async function sdk_EIP2255_wallet_getPermissions(origin: string): Promise<EIP2255Permission[]> {
const wallets = await getAllConnectedWallets(origin, 'sdk')
if (!wallets.size) return []
return EIP2255PermissionsOfWallets(origin, wallets)
}
const requests = new Map<
string,
{
origin: string
request: EIP2255PermissionRequest
promise: DeferTuple<Result<EIP2255RequestedPermission[], MaskEthereumProviderRpcError>>
}
>()
export async function sdk_EIP2255_wallet_requestPermissions(
origin: string,
request: EIP2255PermissionRequest,
): Promise<Result<EIP2255RequestedPermission[], MaskEthereumProviderRpcError>> {
assertOrigin(origin)
for (const method in request) {
if (method !== 'eth_accounts') {
throw err.wallet_requestPermissions.permission_request_contains_unsupported_permission_permission({
permission: method,
})
}
}
const id = Math.random().toString(36).slice(2)
requests.set(id, {
origin,
request,
promise: defer(),
})

await openPopupWindow(PopupRoutes.SelectWallet, {
chainId: ChainId.Mainnet,
external_request: id,
})
return requests.get(id)!.promise[0]
}
export async function sdk_getEIP2255PermissionRequestDetail(id: string) {
return omit(requests.get(id), 'promise')
}
export async function sdk_grantEIP2255Permission(id: string, grantedWalletAddress: Iterable<string>) {
if (!requests.has(id)) throw new Error('Invalid request id')
const { origin, promise } = requests.get(id)!
export async function sdk_grantEIP2255Permission(origin: string, grantedWalletAddress: Iterable<string>) {
enableMapSet()
for (const wallet of grantedWalletAddress) {
const data = await walletDatabase.get('granted_permission', wallet)
Expand All @@ -82,14 +33,7 @@ export async function sdk_grantEIP2255Permission(id: string, grantedWalletAddres
)
if (data !== newData) await walletDatabase.add(newData)
}
promise[1](Ok(EIP2255PermissionsOfWallets(origin, grantedWalletAddress)))
}

export async function sdk_denyEIP2255Permission(id: string) {
if (!requests.has(id)) throw new Error('Invalid request id')
const { promise } = requests.get(id)!
enableMapSet()
promise[1](Err(err.user_rejected_the_request()))
return EIP2255PermissionsOfWallets(origin, grantedWalletAddress)
}

export async function disconnectWalletFromOrigin(wallet: string, origin: string, type: 'any' | 'sdk' | 'internal') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { first, sortBy, uniq } from 'lodash-es'
import urlcat from 'urlcat'
import { memo, useCallback, useMemo, useState } from 'react'
import { useAsyncFn } from 'react-use'
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'
import { useLocation, useNavigate } from 'react-router-dom'
import * as web3_utils from /* webpackDefer: true */ 'web3-utils'
import { useQueries, useQuery } from '@tanstack/react-query'
import { delay } from '@masknet/kit'
Expand Down Expand Up @@ -83,8 +83,6 @@ export const Component = memo(function AddDeriveWallet() {
password: string
isReset: boolean
}
const [params] = useSearchParams()
const external_request = params.get('external_request')

const { mnemonic, password, isReset } = state
// Avoid leaking mnemonic to react-query
Expand Down Expand Up @@ -179,8 +177,8 @@ export const Component = memo(function AddDeriveWallet() {
await Services.Wallet.resolveMaskAccount([{ address: firstWallet }])
Telemetry.captureEvent(EventType.Access, EventID.EntryPopupWalletImport)
await delay(300) // Wait for warming up above. 300ms is the closed duration after testing.
navigate(urlcat(DashboardRoutes.SignUpMaskWalletOnboarding, { external_request }), { replace: true })
}, [mnemonic, wallets.length, isReset, password, mergedIndexes, external_request])
navigate(urlcat(DashboardRoutes.SignUpMaskWalletOnboarding, {}), { replace: true })
}, [mnemonic, wallets.length, isReset, password, mergedIndexes])

const onCheck = useCallback(async (checked: boolean, pathIndex: number) => {
setPathIndexes((list) => {
Expand All @@ -190,8 +188,8 @@ export const Component = memo(function AddDeriveWallet() {
}, [])

const handleRecovery = useCallback(() => {
navigate(urlcat(DashboardRoutes.CreateMaskWalletMnemonic, { external_request }))
}, [navigate, external_request])
navigate(urlcat(DashboardRoutes.CreateMaskWalletMnemonic, {}))
}, [navigate])

const disabled = confirmLoading || isPending || !mergedIndexes.length

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { memo, useCallback, useMemo, useRef, useState } from 'react'
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'
import { useLocation, useNavigate } from 'react-router-dom'
import { useAsync, useAsyncFn } from 'react-use'
import urlcat from 'urlcat'
import { toBlob } from 'html-to-image'
Expand Down Expand Up @@ -185,8 +185,6 @@ export const Component = memo(function CreateMnemonic() {
const { handlePasswordAndWallets } = ResetWalletContext.useContainer()
const [verified, setVerified] = useState(false)
const { classes, cx } = useStyles()
const [params] = useSearchParams()
const external_request = params.get('external_request')
const { words, refreshCallback, puzzleWordList, answerCallback, puzzleAnswer, verifyAnswerCallback, isMatched } =
useMnemonicWordsPuzzle()

Expand All @@ -195,13 +193,13 @@ export const Component = memo(function CreateMnemonic() {
}, [])

const handleRecovery = useCallback(() => {
navigate(urlcat(DashboardRoutes.RecoveryMaskWallet, { external_request }), {
navigate(urlcat(DashboardRoutes.RecoveryMaskWallet, {}), {
state: {
password: location.state?.password,
isReset: location.state?.isReset,
},
})
}, [location.state?.password, location.state?.isReset, external_request])
}, [location.state?.password, location.state?.isReset])

const { value: hasPassword, loading: loadingHasPassword } = useAsync(Services.Wallet.hasPassword, [])

Expand All @@ -226,8 +224,8 @@ export const Component = memo(function CreateMnemonic() {
})
await Services.Wallet.resolveMaskAccount([{ address }])
Telemetry.captureEvent(EventType.Access, EventID.EntryPopupWalletCreate)
navigate(urlcat(DashboardRoutes.SignUpMaskWalletOnboarding, { external_request }), { replace: true })
}, [walletName, words, location.state?.isReset, location.state?.password, external_request])
navigate(urlcat(DashboardRoutes.SignUpMaskWalletOnboarding, {}), { replace: true })
}, [walletName, words, location.state?.isReset, location.state?.password])

const step = useMemo(() => String((verified ? 3 : 2) - (hasPassword ? 1 : 0)), [verified, hasPassword])
const totalSteps = hasPassword ? '2' : '3'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ export const Component = memo(function CreateWalletForm() {
const params = new URLSearchParams(location.search)
const isReset = params.get('reset')
const isRecover = params.get('recover')
const external_request = params.get('external_request')

const schema = useMemo(() => {
const passwordRule = zod
Expand Down Expand Up @@ -100,9 +99,7 @@ export const Component = memo(function CreateWalletForm() {

const onSubmit = handleSubmit((data) => {
navigate(
urlcat(isRecover ? DashboardRoutes.RecoveryMaskWallet : DashboardRoutes.CreateMaskWalletMnemonic, {
external_request,
}),
urlcat(isRecover ? DashboardRoutes.RecoveryMaskWallet : DashboardRoutes.CreateMaskWalletMnemonic, {}),
data.password ?
{
state: { password: data.password, isReset },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { PopupRoutes } from '@masknet/shared-base'

import Services from '#services'
import { OnboardingWriter } from '../../../components/OnboardingWriter/index.js'
import { useSearchParams } from 'react-router-dom'

const useStyles = makeStyles()((theme) => ({
card: {
Expand Down Expand Up @@ -69,17 +68,11 @@ const useStyles = makeStyles()((theme) => ({
export const Component = memo(function Onboarding() {
const t = useDashboardTrans()
const { classes } = useStyles()
const [params] = useSearchParams()
const external_request = params.get('external_request')

const onOpenPopupWallet = useCallback(async () => {
if (external_request) {
await Services.Helper.openPopupWindow(PopupRoutes.SelectWallet, { external_request })
} else {
await Services.Helper.openPopupWindow(PopupRoutes.Wallet, {})
}
await Services.Helper.openPopupWindow(PopupRoutes.Wallet, {})
window.close()
}, [external_request])
}, [])

const words = useMemo(() => {
return [
Expand Down
20 changes: 9 additions & 11 deletions packages/mask/dashboard/pages/CreateMaskWallet/Recovery/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Tab, Typography } from '@mui/material'
import { Box } from '@mui/system'
import { memo, use, useCallback, useMemo, useState } from 'react'
import type { UseFormSetError } from 'react-hook-form'
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'
import { useLocation, useNavigate } from 'react-router-dom'
import { useAsync } from 'react-use'
import { RestoreFromMnemonic } from '../../../components/Restore/RestoreFromMnemonic.js'
import { RestoreFromPrivateKey, type FormInputs } from '../../../components/Restore/RestoreFromPrivateKey.js'
Expand Down Expand Up @@ -91,8 +91,6 @@ export const Component = memo(function Recovery() {
const navigate = useNavigate()
const [error, setError] = useState('')
const { handlePasswordAndWallets } = ResetWalletContext.useContainer()
const [params] = useSearchParams()
const external_request = params.get('external_request')

const [currentTab, onChange, tabs] = useTabs('mnemonic', 'privateKey', 'local')

Expand All @@ -111,7 +109,7 @@ export const Component = memo(function Recovery() {
const mnemonic = values.join(' ')
await Services.Wallet.getDerivableAccounts(mnemonic, 0, 1)

navigate(urlcat(DashboardRoutes.AddDeriveWallet, { external_request }), {
navigate(urlcat(DashboardRoutes.AddDeriveWallet, {}), {
replace: false,
state: {
mnemonic,
Expand All @@ -127,7 +125,7 @@ export const Component = memo(function Recovery() {
)
}
},
[t, navigate, location.state?.isReset, location.state?.password, external_request],
[t, navigate, location.state?.isReset, location.state?.password],
)

const { NameService } = useWeb3State(NetworkPluginID.PLUGIN_EVM)
Expand All @@ -146,7 +144,7 @@ export const Component = memo(function Recovery() {
silent: true,
})
Telemetry.captureEvent(EventType.Access, EventID.EntryPopupWalletImport)
navigate(urlcat(DashboardRoutes.SignUpMaskWalletOnboarding, { external_request }), { replace: true })
navigate(urlcat(DashboardRoutes.SignUpMaskWalletOnboarding, {}), { replace: true })
} catch (error) {
const errorMsg = (error as Error).message
onError('privateKey', {
Expand All @@ -155,7 +153,7 @@ export const Component = memo(function Recovery() {
})
}
},
[t, navigate, location.state?.isReset, location.state?.password, newWalletName, external_request],
[t, navigate, location.state?.isReset, location.state?.password, newWalletName],
)

const onRestore = useCallback(
Expand All @@ -182,7 +180,7 @@ export const Component = memo(function Recovery() {
})
await Services.Wallet.resolveMaskAccount([{ address }])
Telemetry.captureEvent(EventType.Access, EventID.EntryPopupWalletImport)
navigate(urlcat(DashboardRoutes.SignUpMaskWalletOnboarding, { external_request }), { replace: true })
navigate(urlcat(DashboardRoutes.SignUpMaskWalletOnboarding, {}), { replace: true })
} catch (error) {
const errorMsg = (error as Error).message
// Todo: SDK should return 'Incorrect Keystore Password.' when keystore pwd is wrong.
Expand All @@ -193,18 +191,18 @@ export const Component = memo(function Recovery() {
)
}
},
[t, navigate, location.state?.isReset, location.state?.password, newWalletName, external_request],
[t, navigate, location.state?.isReset, location.state?.password, newWalletName],
)

const handleRecovery = useCallback(() => {
navigate(urlcat(DashboardRoutes.CreateMaskWalletMnemonic, { external_request }), {
navigate(urlcat(DashboardRoutes.CreateMaskWalletMnemonic, {}), {
state: {
password: location.state?.password,
isReset: location.state?.isReset,
},
replace: true,
})
}, [location.state?.password, location.state?.isReset, external_request])
}, [location.state?.password, location.state?.isReset])

const { value: hasPassword, loading: loadingHasPassword } = useAsync(Services.Wallet.hasPassword, [])

Expand Down
18 changes: 12 additions & 6 deletions packages/mask/entry-sdk/bridge/eth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { readonlyMethodType, EthereumMethodType, ProviderType, ChainId } from '@masknet/web3-shared-evm'
import Services from '#services'
import { type EIP2255PermissionRequest, MaskEthereumProviderRpcError, err } from '@masknet/sdk'
import { MaskEthereumProviderRpcError, err } from '@masknet/sdk'
import { Err, Ok } from 'ts-results-es'
import { isSameAddress } from '@masknet/web3-shared-base'
import * as providers from /* webpackDefer: true */ '@masknet/web3-providers'
Expand Down Expand Up @@ -176,15 +176,21 @@ const methods: Methods = {
if (Object.keys(request).length === 0)
throw err.wallet_requestPermissions.a_permission_request_must_contain_at_least_1_permission()
for (const key in request) {
if (typeof key !== 'string' || typeof request[key] !== 'object' || request[key] === null)
if (key !== 'eth_accounts' || typeof request[key] !== 'object' || request[key] === null)
throw err.wallet_requestPermissions.permission_request_contains_unsupported_permission_permission({
permission: key,
})
}
return Services.Wallet.sdk_EIP2255_wallet_requestPermissions(
location.origin,
request as EIP2255PermissionRequest,
)

const p = providers.EVMWeb3.getWeb3Provider({
providerType: ProviderType.MaskWallet,
silent: false,
readonly: false,
})
return p.request({
method: EthereumMethodType.wallet_requestPermissions,
params: [request],
})
},
wallet_revokePermissions: null!,
wallet_switchEthereumChain: null!,
Expand Down
Loading

0 comments on commit 87a62d4

Please sign in to comment.