Skip to content
5 changes: 5 additions & 0 deletions .changeset/four-pants-notice.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wagmi": minor
---

Added `useRequestPermissions` hook.
5 changes: 5 additions & 0 deletions .changeset/seven-terms-guess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@wagmi/core": minor
---

Added `requestPermissions` action.
30 changes: 30 additions & 0 deletions packages/core/src/actions/requestPermissions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { accounts, config } from '@wagmi/test'
import { expect, test } from 'vitest'

import { connect } from './connect.js'
import { disconnect } from './disconnect.js'
import { requestPermissions } from './requestPermissions.js'

const connector = config.connectors[0]!

test('default', async () => {
await connect(config, { connector })
try {
await expect(
requestPermissions(config, { eth_accounts: {} }),
).resolves.toEqual([
{
caveats: [
{
type: 'filterResponse',
value: accounts,
},
],
invoker: 'https://example.com',
parentCapability: 'eth_accounts',
},
])
} finally {
await disconnect(config, { connector })
}
})
46 changes: 46 additions & 0 deletions packages/core/src/actions/requestPermissions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {
type RequestPermissionsErrorType as viem_RequestPermissionsErrorType,
type RequestPermissionsParameters as viem_RequestPermissionsParameters,
type RequestPermissionsReturnType as viem_RequestPermissionsReturnType,
requestPermissions as viem_requestPermissions,
} from 'viem/actions'
import type { Config } from '../createConfig.js'
import type { BaseErrorType, ErrorType } from '../errors/base.js'
import type { ConnectorParameter } from '../types/properties.js'
import type { Compute } from '../types/utils.js'
import { getAction } from '../utils/getAction.js'
import {
type GetConnectorClientErrorType,
getConnectorClient,
} from './getConnectorClient.js'

export type RequestPermissionsParameters = Compute<
viem_RequestPermissionsParameters & ConnectorParameter
>

export type RequestPermissionsReturnType = viem_RequestPermissionsReturnType

export type RequestPermissionsErrorType =
// getConnectorClient()
| GetConnectorClientErrorType
// base
| BaseErrorType
| ErrorType
// viem
| viem_RequestPermissionsErrorType

/** https://wagmi.sh/core/api/actions/requestPermissions */
export async function requestPermissions(
config: Config,
parameters: RequestPermissionsParameters,
): Promise<RequestPermissionsReturnType> {
const { connector, ...rest } = parameters
const client = await getConnectorClient(config, { connector })

Check warning on line 38 in packages/core/src/actions/requestPermissions.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/actions/requestPermissions.ts#L37-L38

Added lines #L37 - L38 were not covered by tests

const action = getAction(

Check warning on line 40 in packages/core/src/actions/requestPermissions.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/actions/requestPermissions.ts#L40

Added line #L40 was not covered by tests
client,
viem_requestPermissions,
'requestPermissions',
)
return action(rest)

Check warning on line 45 in packages/core/src/actions/requestPermissions.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/actions/requestPermissions.ts#L45

Added line #L45 was not covered by tests
}
13 changes: 13 additions & 0 deletions packages/core/src/connectors/mock.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ test('behavior: connector.getProvider request errors', async () => {
const connectorFn = mock({
accounts,
features: {
requestPermissionsError: true,
signMessageError: true,
signTypedDataError: true,
switchChainError: true,
Expand All @@ -83,6 +84,18 @@ test('behavior: connector.getProvider request errors', async () => {
) as ReturnType<typeof connectorFn>
const provider = await connector.getProvider()

await expect(
provider.request({
method: 'wallet_requestPermissions',
params: [{ eth_accounts: {} }],
}),
).rejects.toThrowErrorMatchingInlineSnapshot(`
[UserRejectedRequestError: User rejected the request.

Details: Failed to request permissions.
Version: viem@2.55.7]
`)

await expect(
provider.request({
method: 'eth_signTypedData_v4',
Expand Down
23 changes: 23 additions & 0 deletions packages/core/src/connectors/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
signMessageError?: boolean | Error | undefined
signTypedDataError?: boolean | Error | undefined
reconnect?: boolean | undefined
requestPermissionsError?: boolean | Error | undefined
watchAssetError?: boolean | Error | undefined
}
| undefined
Expand Down Expand Up @@ -176,6 +177,28 @@
}

// wallet methods
if (method === 'wallet_requestPermissions') {
if (features.requestPermissionsError) {
if (typeof features.requestPermissionsError === 'boolean')
throw new UserRejectedRequestError(

Check warning on line 183 in packages/core/src/connectors/mock.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/connectors/mock.ts#L183

Added line #L183 was not covered by tests
new Error('Failed to request permissions.'),
)
throw features.requestPermissionsError

Check warning on line 186 in packages/core/src/connectors/mock.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/connectors/mock.ts#L186

Added line #L186 was not covered by tests
}
return [

Check warning on line 188 in packages/core/src/connectors/mock.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/connectors/mock.ts#L188

Added line #L188 was not covered by tests
{
invoker: 'https://example.com',
parentCapability: 'eth_accounts',
caveats: [
{
type: 'filterResponse',
value: parameters.accounts,
},
],
},
]
}

if (method === 'wallet_switchEthereumChain') {
if (features.switchChainError) {
if (typeof features.switchChainError === 'boolean')
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/exports/actions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ test('exports', () => {
"readContract",
"readContracts",
"reconnect",
"requestPermissions",
"sendCalls",
"sendTransaction",
"showCallsStatus",
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/exports/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,13 @@ export {
reconnect,
} from '../actions/reconnect.js'

export {
type RequestPermissionsErrorType,
type RequestPermissionsParameters,
type RequestPermissionsReturnType,
requestPermissions,
} from '../actions/requestPermissions.js'

export {
type SendCallsErrorType,
type SendCallsParameters,
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/exports/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ test('exports', () => {
"readContract",
"readContracts",
"reconnect",
"requestPermissions",
"sendCalls",
"sendCallsSync",
"sendTransaction",
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/exports/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,13 @@ export {
reconnect,
} from '../actions/reconnect.js'

export {
type RequestPermissionsErrorType,
type RequestPermissionsParameters,
type RequestPermissionsReturnType,
requestPermissions,
} from '../actions/requestPermissions.js'

export {
type SendCallsErrorType,
type SendCallsParameters,
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/exports/query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ test('exports', () => {
"readContractsQueryKey",
"readContractsQueryOptions",
"reconnectMutationOptions",
"requestPermissionsMutationOptions",
"sendCallsMutationOptions",
"sendCallsSyncMutationOptions",
"sendTransactionMutationOptions",
Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/exports/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,16 @@ export {
reconnectMutationOptions,
} from '../query/reconnect.js'

export {
type RequestPermissionsData,
type RequestPermissionsMutate,
type RequestPermissionsMutateAsync,
type RequestPermissionsMutationOptions,
type RequestPermissionsOptions,
type RequestPermissionsVariables,
requestPermissionsMutationOptions,
} from '../query/requestPermissions.js'

export {
type SendCallsData,
type SendCallsMutate,
Expand Down
45 changes: 45 additions & 0 deletions packages/core/src/query/requestPermissions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { accounts, config } from '@wagmi/test'
import { expect, test } from 'vitest'

import { connect } from '../actions/connect.js'
import { disconnect } from '../actions/disconnect.js'
import { requestPermissionsMutationOptions } from './requestPermissions.js'

const connector = config.connectors[0]!

test('default', () => {
expect(requestPermissionsMutationOptions(config)).toMatchInlineSnapshot(`
{
"mutationFn": [Function],
"mutationKey": [
"requestPermissions",
],
}
`)
})

test('behavior: mutationFn', async () => {
await connect(config, { connector })
try {
const options = requestPermissionsMutationOptions(config)
await expect(
options.mutationFn?.({
connector,
eth_accounts: {},
}),
).resolves.toEqual([
{
caveats: [
{
type: 'filterResponse',
value: accounts,
},
],
invoker: 'https://example.com',
parentCapability: 'eth_accounts',
},
])
} finally {
await disconnect(config, { connector })
}
})
55 changes: 55 additions & 0 deletions packages/core/src/query/requestPermissions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type { MutationOptions } from '@tanstack/query-core'
import {
type RequestPermissionsErrorType,
type RequestPermissionsParameters,
type RequestPermissionsReturnType,
requestPermissions,
} from '../actions/requestPermissions.js'
import type { Config } from '../createConfig.js'
import type { MutationParameter } from '../types/query.js'
import type { Compute } from '../types/utils.js'
import type { Mutate, MutateAsync } from './types.js'

export type RequestPermissionsOptions<context = unknown> = MutationParameter<
RequestPermissionsData,
RequestPermissionsErrorType,
RequestPermissionsVariables,
context
>

export function requestPermissionsMutationOptions<context>(
config: Config,
options: RequestPermissionsOptions<context> = {},
): RequestPermissionsMutationOptions {
return {

Check warning on line 24 in packages/core/src/query/requestPermissions.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/query/requestPermissions.ts#L24

Added line #L24 was not covered by tests
...(options.mutation as any),
mutationFn(variables) {
return requestPermissions(config, variables)

Check warning on line 27 in packages/core/src/query/requestPermissions.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/query/requestPermissions.ts#L27

Added line #L27 was not covered by tests
},
mutationKey: ['requestPermissions'],
}
}

export type RequestPermissionsMutationOptions = MutationOptions<
RequestPermissionsData,
RequestPermissionsErrorType,
RequestPermissionsVariables
>

export type RequestPermissionsData = RequestPermissionsReturnType

export type RequestPermissionsVariables = Compute<RequestPermissionsParameters>

export type RequestPermissionsMutate<context = unknown> = Mutate<
RequestPermissionsData,
RequestPermissionsErrorType,
RequestPermissionsVariables,
context
>

export type RequestPermissionsMutateAsync<context = unknown> = MutateAsync<
RequestPermissionsData,
RequestPermissionsErrorType,
RequestPermissionsVariables,
context
>
1 change: 1 addition & 0 deletions packages/react/src/exports/actions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ test('exports', () => {
"readContract",
"readContracts",
"reconnect",
"requestPermissions",
"sendCalls",
"sendTransaction",
"showCallsStatus",
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/exports/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ test('exports', () => {
"useReadContract",
"useReadContracts",
"useReconnect",
"useRequestPermissions",
"useSendCalls",
"useSendCallsSync",
"useSendTransaction",
Expand Down
6 changes: 6 additions & 0 deletions packages/react/src/exports/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,12 @@ export {
useReconnect,
} from '../hooks/useReconnect.js'

export {
type UseRequestPermissionsParameters,
type UseRequestPermissionsReturnType,
useRequestPermissions,
} from '../hooks/useRequestPermissions.js'

export {
type UseSendCallsParameters,
type UseSendCallsReturnType,
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/exports/query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ test('exports', () => {
"readContractsQueryKey",
"readContractsQueryOptions",
"reconnectMutationOptions",
"requestPermissionsMutationOptions",
"sendCallsMutationOptions",
"sendCallsSyncMutationOptions",
"sendTransactionMutationOptions",
Expand Down
Loading
Loading