Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: addChain #4313

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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: 5 additions & 0 deletions .changeset/fast-cooks-provide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@wagmi/core": minor
---

Added `addChain` action.
5 changes: 5 additions & 0 deletions .changeset/thin-rings-carry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wagmi": minor
---

Added `useAddChain` hook.
25 changes: 25 additions & 0 deletions packages/core/src/actions/addChain.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { config } from '@wagmi/test'
import { avalanche } from 'viem/chains'
import { test } from 'vitest'

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

const connector = config.connectors[0]!

test('default', async () => {
await connect(config, { connector })
await addChain(config, {
chain: avalanche,
})
await disconnect(config, { connector })
})

test('no block explorer', async () => {
await connect(config, { connector })
await addChain(config, {
chain: { ...avalanche, blockExplorers: undefined },
})
await disconnect(config, { connector })
})
38 changes: 38 additions & 0 deletions packages/core/src/actions/addChain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {
type AddChainErrorType as viem_AddChainErrorType,
type AddChainParameters as viem_AddChainParameters,
addChain as viem_addChain,
} 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 AddChainParameters = Compute<
viem_AddChainParameters & ConnectorParameter
>

export type AddChainErrorType =
// getConnectorClient()
| GetConnectorClientErrorType
// base
| BaseErrorType
| ErrorType
// viem
| viem_AddChainErrorType

/** https://wagmi.sh/core/api/actions/addChain */
export async function addChain(config: Config, parameters: AddChainParameters) {
const { connector, ...rest } = parameters

const client = await getConnectorClient(config, { connector })

const action = getAction(client, viem_addChain, 'addChain')
return action(rest)
}
12 changes: 12 additions & 0 deletions packages/core/src/connectors/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type MockParameters = {
features?:
| {
connectError?: boolean | Error | undefined
addChainError?: boolean | Error | undefined
switchChainError?: boolean | Error | undefined
signMessageError?: boolean | Error | undefined
signTypedDataError?: boolean | Error | undefined
Expand Down Expand Up @@ -145,6 +146,17 @@ export function mock(parameters: MockParameters) {
}

// wallet methods
if (method === 'wallet_addEthereumChain') {
if (features.addChainError) {
if (typeof features.addChainError === 'boolean')
throw new UserRejectedRequestError(
new Error('Failed to add chain.'),
)
throw features.addChainError
}
return
}

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 @@ -5,6 +5,7 @@ import * as actions from './actions.js'
test('exports', () => {
expect(Object.keys(actions)).toMatchInlineSnapshot(`
[
"addChain",
"call",
"connect",
"deployContract",
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/exports/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
////////////////////////////////////////////////////////////////////////////////

// biome-ignore lint/performance/noBarrelFile: entrypoint module
export {
type AddChainErrorType,
type AddChainParameters,
addChain,
} from '../actions/addChain.js'

export {
type CallErrorType,
type CallParameters,
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 @@ -5,6 +5,7 @@ import * as core from './index.js'
test('exports', () => {
expect(Object.keys(core)).toMatchInlineSnapshot(`
[
"addChain",
"call",
"connect",
"deployContract",
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/exports/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
////////////////////////////////////////////////////////////////////////////////

// biome-ignore lint/performance/noBarrelFile: entrypoint module
export {
type AddChainErrorType,
type AddChainParameters,
addChain,
} from '../actions/addChain.js'

export {
type CallErrorType,
type CallParameters,
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 @@ -5,6 +5,7 @@ import * as query from './query.js'
test('exports', () => {
expect(Object.keys(query)).toMatchInlineSnapshot(`
[
"addChainMutationOptions",
"callQueryKey",
"callQueryOptions",
"connectMutationOptions",
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/exports/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
////////////////////////////////////////////////////////////////////////////////

// biome-ignore lint/performance/noBarrelFile: entrypoint module
export {
type AddChainData,
type AddChainVariables,
type AddChainMutate,
type AddChainMutateAsync,
addChainMutationOptions,
} from '../query/addChain.js'

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

import { addChainMutationOptions } from './addChain.js'

test('default', () => {
expect(addChainMutationOptions(config)).toMatchInlineSnapshot(`
{
"mutationFn": [Function],
"mutationKey": [
"addChain",
],
}
`)
})
40 changes: 40 additions & 0 deletions packages/core/src/query/addChain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { MutationOptions } from '@tanstack/query-core'

import {
type AddChainErrorType,
type AddChainParameters,
addChain,
} from '../actions/addChain.js'
import type { Config } from '../createConfig.js'
import type { Mutate, MutateAsync } from './types.js'

export function addChainMutationOptions(config: Config) {
return {
mutationFn(variables) {
return addChain(config, variables)
},
mutationKey: ['addChain'],
} as const satisfies MutationOptions<
AddChainData,
AddChainErrorType,
AddChainVariables
>
}

export type AddChainData = void

export type AddChainVariables = AddChainParameters

export type AddChainMutate<context = unknown> = Mutate<
AddChainData,
AddChainErrorType,
AddChainVariables,
context
>

export type AddChainMutateAsync<context = unknown> = MutateAsync<
AddChainData,
AddChainErrorType,
AddChainVariables,
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 @@ -5,6 +5,7 @@ import * as actions from './actions.js'
test('exports', () => {
expect(Object.keys(actions)).toMatchInlineSnapshot(`
[
"addChain",
"call",
"connect",
"deployContract",
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 @@ -13,6 +13,7 @@ test('exports', () => {
"WagmiProviderNotFoundError",
"useAccount",
"useAccountEffect",
"useAddChain",
"useBalance",
"useBlock",
"useBlockNumber",
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 @@ -39,6 +39,12 @@ export {
useAccountEffect,
} from '../hooks/useAccountEffect.js'

export {
type UseAddChainParameters,
type UseAddChainReturnType,
useAddChain,
} from '../hooks/useAddChain.js'

export {
type UseBalanceParameters,
type UseBalanceReturnType,
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 @@ -5,6 +5,7 @@ import * as query from './query.js'
test('exports', () => {
expect(Object.keys(query)).toMatchInlineSnapshot(`
[
"addChainMutationOptions",
"callQueryKey",
"callQueryOptions",
"connectMutationOptions",
Expand Down
62 changes: 62 additions & 0 deletions packages/react/src/hooks/useAddChain.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import type { AddChainErrorType } from '@wagmi/core'
import type { AddChainVariables } from '@wagmi/core/query'
import { expectTypeOf, test } from 'vitest'

import { avalanche } from 'viem/chains'
import { useAddChain } from './useAddChain.js'

const contextValue = { foo: 'bar' } as const

test('context', () => {
const { context, data, error, addChain, variables } = useAddChain({
mutation: {
onMutate(variables) {
expectTypeOf(variables).toEqualTypeOf<AddChainVariables>()
return contextValue
},
onError(error, variables, context) {
expectTypeOf(variables).toEqualTypeOf<AddChainVariables>()
expectTypeOf(error).toEqualTypeOf<AddChainErrorType>()
expectTypeOf(context).toEqualTypeOf<typeof contextValue | undefined>()
},
onSuccess(data, variables, context) {
expectTypeOf(variables).toEqualTypeOf<AddChainVariables>()
expectTypeOf(data).toEqualTypeOf<void>()
expectTypeOf(context).toEqualTypeOf<typeof contextValue>()
},
onSettled(data, error, variables, context) {
expectTypeOf(data).toEqualTypeOf<void | undefined>()
expectTypeOf(error).toEqualTypeOf<AddChainErrorType | null>()
expectTypeOf(variables).toEqualTypeOf<AddChainVariables>()
expectTypeOf(context).toEqualTypeOf<typeof contextValue | undefined>()
},
},
})

expectTypeOf(data).toEqualTypeOf<void | undefined>()
expectTypeOf(error).toEqualTypeOf<AddChainErrorType | null>()
expectTypeOf(variables).toEqualTypeOf<AddChainVariables | undefined>()
expectTypeOf(context).toEqualTypeOf<typeof contextValue | undefined>()

addChain(
{ chain: avalanche },
{
onError(error, variables, context) {
expectTypeOf(variables).toEqualTypeOf<AddChainVariables>()
expectTypeOf(error).toEqualTypeOf<AddChainErrorType>()
expectTypeOf(context).toEqualTypeOf<typeof contextValue | undefined>()
},
onSuccess(data, variables, context) {
expectTypeOf(variables).toEqualTypeOf<AddChainVariables>()
expectTypeOf(data).toEqualTypeOf<void>()
expectTypeOf(context).toEqualTypeOf<typeof contextValue>()
},
onSettled(data, error, variables, context) {
expectTypeOf(data).toEqualTypeOf<void | undefined>()
expectTypeOf(error).toEqualTypeOf<AddChainErrorType | null>()
expectTypeOf(variables).toEqualTypeOf<AddChainVariables>()
expectTypeOf(context).toEqualTypeOf<typeof contextValue | undefined>()
},
},
)
})
33 changes: 33 additions & 0 deletions packages/react/src/hooks/useAddChain.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { connect, disconnect } from '@wagmi/core'
import { config } from '@wagmi/test'
import { renderHook, waitFor } from '@wagmi/test/react'
import { avalanche } from 'viem/chains'
import { expect, test } from 'vitest'

import { useAddChain } from './useAddChain.js'

const connector = config.connectors[0]!

test('default', async () => {
await connect(config, { connector })

const { result } = renderHook(() => useAddChain())

result.current.addChain({ chain: avalanche })
await waitFor(() => expect(result.current.isSuccess).toBeTruthy())

await disconnect(config, { connector })
})

test('no block explorer', async () => {
await connect(config, { connector })

const { result } = renderHook(() => useAddChain())

result.current.addChain({
chain: { ...avalanche, blockExplorers: undefined },
})
await waitFor(() => expect(result.current.isSuccess).toBeTruthy())

await disconnect(config, { connector })
})
Loading
Loading