From 7567009a3285bb981c453e047ca53b23ef7277c2 Mon Sep 17 00:00:00 2001 From: Vitor Date: Tue, 8 Oct 2024 00:04:57 -0300 Subject: [PATCH 1/4] feat: sign transaction --- .../src/actions/signTransaction.test-d.ts | 50 +++ .../core/src/actions/signTransaction.test.ts | 84 +++++ packages/core/src/actions/signTransaction.ts | 112 ++++++ packages/core/src/exports/actions.test.ts | 1 + packages/core/src/exports/actions.ts | 7 + packages/core/src/exports/index.test.ts | 1 + packages/core/src/exports/index.ts | 7 + packages/core/src/exports/query.test.ts | 1 + packages/core/src/exports/query.ts | 8 + .../core/src/query/signTransaction.test.ts | 15 + packages/core/src/query/signTransaction.ts | 65 ++++ packages/react/src/exports/actions.test.ts | 1 + packages/react/src/exports/index.test.ts | 1 + packages/react/src/exports/index.ts | 6 + packages/react/src/exports/query.test.ts | 1 + .../src/hooks/useSignTransaction.test-d.ts | 78 ++++ .../src/hooks/useSignTransaction.test.ts | 34 ++ .../react/src/hooks/useSignTransaction.ts | 79 ++++ site/.vitepress/sidebar.ts | 8 + site/core/api/actions/signTransaction.md | 342 ++++++++++++++++++ site/react/api/hooks/useSignTransaction.md | 93 +++++ 21 files changed, 994 insertions(+) create mode 100644 packages/core/src/actions/signTransaction.test-d.ts create mode 100644 packages/core/src/actions/signTransaction.test.ts create mode 100644 packages/core/src/actions/signTransaction.ts create mode 100644 packages/core/src/query/signTransaction.test.ts create mode 100644 packages/core/src/query/signTransaction.ts create mode 100644 packages/react/src/hooks/useSignTransaction.test-d.ts create mode 100644 packages/react/src/hooks/useSignTransaction.test.ts create mode 100644 packages/react/src/hooks/useSignTransaction.ts create mode 100644 site/core/api/actions/signTransaction.md create mode 100644 site/react/api/hooks/useSignTransaction.md diff --git a/packages/core/src/actions/signTransaction.test-d.ts b/packages/core/src/actions/signTransaction.test-d.ts new file mode 100644 index 0000000000..e4b23178d9 --- /dev/null +++ b/packages/core/src/actions/signTransaction.test-d.ts @@ -0,0 +1,50 @@ +import { http, parseEther } from 'viem' +import { celo, mainnet } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { createConfig } from '../createConfig.js' +import { + type SignTransactionParameters, + signTransaction, +} from './signTransaction.js' + +test('chain formatters', () => { + const config = createConfig({ + chains: [mainnet, celo], + transports: { [celo.id]: http(), [mainnet.id]: http() }, + }) + + type Result = SignTransactionParameters + expectTypeOf().toMatchTypeOf<{ + chainId?: typeof celo.id | typeof mainnet.id | undefined + feeCurrency?: `0x${string}` | undefined + }>() + signTransaction(config, { + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + feeCurrency: '0x', + }) + + type Result2 = SignTransactionParameters + expectTypeOf().toMatchTypeOf<{ + feeCurrency?: `0x${string}` | undefined + }>() + signTransaction(config, { + chainId: celo.id, + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + feeCurrency: '0x', + }) + + type Result3 = SignTransactionParameters + expectTypeOf().not.toMatchTypeOf<{ + feeCurrency?: `0x${string}` | undefined + }>() + signTransaction(config, { + chainId: mainnet.id, + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + // @ts-expect-error + feeCurrency: '0x', + }) +}) diff --git a/packages/core/src/actions/signTransaction.test.ts b/packages/core/src/actions/signTransaction.test.ts new file mode 100644 index 0000000000..2e74e25427 --- /dev/null +++ b/packages/core/src/actions/signTransaction.test.ts @@ -0,0 +1,84 @@ +import { config, privateKey } from '@wagmi/test' +import { parseEther, type TransactionRequestBase } from 'viem' +import { beforeEach, expect, test } from 'vitest' + +import { privateKeyToAccount } from 'viem/accounts' +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { signTransaction } from './signTransaction.js' + +const connector = config.connectors[0]! + +const base = { + from: '0x0000000000000000000000000000000000000000', + gas: 21000n, + nonce: 785, +} satisfies TransactionRequestBase + +beforeEach(async () => { + if (config.state.current === connector.uid) + await disconnect(config, { connector }) +}) + +test('default', async () => { + await connect(config, { connector }) + await expect( + signTransaction(config, { + // ...base, + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + }), + ).resolves.toMatchInlineSnapshot( + '"0x02f870018203118085065e22cad982520894d2135cfb216b74109775236e36d4b433f1df507b872386f26fc1000080c080a0af0d6c8691aae5ecfe11b40f69ea580980175ce3a242b431f65c6192c5f59663a0016d0a36a9b3100da6a45d818a4261d64ad5276d07f6313e816777705e619b91"', + ) + await disconnect(config, { connector }) +}) + +test('behavior: connector not connected', async () => { + await connect(config, { connector }) + await expect( + signTransaction(config, { + ...base, + connector: config.connectors[1], + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [ConnectorNotConnectedError: Connector not connected. + + Version: @wagmi/core@x.y.z] + `) + await disconnect(config, { connector }) +}) + +test('behavior: account does not exist on connector', async () => { + await connect(config, { connector }) + await expect( + signTransaction(config, { + ...base, + account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [ConnectorAccountNotFoundError: Account "0xA0Cf798816D4b9b9866b5330EEa46a18382f251e" not found for connector "Mock Connector". + + Version: @wagmi/core@x.y.z] + `) + await disconnect(config, { connector }) +}) + +test('behavior: local account', async () => { + const account = privateKeyToAccount(privateKey) + await expect( + signTransaction(config, { + ...base, + account, + type: 'eip1559', + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + }), + ).resolves.toMatchInlineSnapshot( + '"0x02f8690182031180808094d2135cfb216b74109775236e36d4b433f1df507b872386f26fc1000080c001a05df802f592b42ca82e8e9fb9ef0a923ef4d090234fd80dd83d980c09948ab5dea03b845f9d0602a1a11fb319f68d77d658d471c6661733179094bd313a4c1b61aa"', + ) +}) diff --git a/packages/core/src/actions/signTransaction.ts b/packages/core/src/actions/signTransaction.ts new file mode 100644 index 0000000000..3bffd396e6 --- /dev/null +++ b/packages/core/src/actions/signTransaction.ts @@ -0,0 +1,112 @@ +import type { + Account, + Chain, + Client, + TransactionRequest, + SignTransactionErrorType as viem_SignTransactionErrorType, + SignTransactionParameters as viem_SignTransactionParameters, + SignTransactionReturnType as viem_SignTransactionReturnType, +} from 'viem' +import { + estimateGas as viem_estimateGas, + signTransaction as viem_signTransaction, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { BaseErrorType, ErrorType } from '../errors/base.js' +import type { SelectChains } from '../types/chain.js' +import type { + ChainIdParameter, + ConnectorParameter, +} from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' +import { getAccount } from './getAccount.js' +import { + type GetConnectorClientErrorType, + getConnectorClient, +} from './getConnectorClient.js' + +export type SignTransactionParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + /// + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + Omit< + viem_SignTransactionParameters, + 'chain' | 'gas' + > & + ChainIdParameter & + ConnectorParameter + > +}[number] & { + /** Gas provided for transaction execution, or `null` to skip the prelude gas estimation. */ + gas?: TransactionRequest['gas'] | null +} + +export type SignTransactionReturnType = viem_SignTransactionReturnType + +export type SignTransactionErrorType = + // getConnectorClient() + | GetConnectorClientErrorType + // base + | BaseErrorType + | ErrorType + // viem + | viem_SignTransactionErrorType + +/** https://wagmi.sh/core/api/actions/signTransaction */ +export async function signTransaction< + config extends Config, + chainId extends config['chains'][number]['id'], +>( + config: config, + parameters: SignTransactionParameters, +): Promise { + const { account, chainId, connector, gas: gas_, ...rest } = parameters + + let client: Client + if (typeof account === 'object' && account.type === 'local') + client = config.getClient({ chainId }) + else + client = await getConnectorClient(config, { account, chainId, connector }) + + const { connector: activeConnector } = getAccount(config) + + const gas = await (async () => { + // Skip gas estimation if `data` doesn't exist (not a contract interaction). + if (!('data' in parameters) || !parameters.data) return undefined + + // Skip gas estimation if connector supports simulation. + if ((connector ?? activeConnector)?.supportsSimulation) return undefined + + // Skip gas estimation if `null` is provided. + if (gas_ === null) return undefined + + // Run gas estimation if no value is provided. + if (gas_ === undefined) { + const action = getAction(client, viem_estimateGas, 'estimateGas') + return action({ + ...(rest as any), + account, + chain: chainId ? { id: chainId } : null, + }) + } + + // Use provided gas value. + return gas_ + })() + + const action = getAction(client, viem_signTransaction, 'signTransaction') + const signature = await action({ + ...(rest as any), + ...(account ? { account } : {}), + gas, + chain: chainId ? { id: chainId } : null, + }) + + return signature +} diff --git a/packages/core/src/exports/actions.test.ts b/packages/core/src/exports/actions.test.ts index 77f805d96c..d6ad2b191b 100644 --- a/packages/core/src/exports/actions.test.ts +++ b/packages/core/src/exports/actions.test.ts @@ -55,6 +55,7 @@ test('exports', () => { "reconnect", "sendTransaction", "signMessage", + "signTransaction", "signTypedData", "simulateContract", "switchAccount", diff --git a/packages/core/src/exports/actions.ts b/packages/core/src/exports/actions.ts index d5d6b21760..480691e0a6 100644 --- a/packages/core/src/exports/actions.ts +++ b/packages/core/src/exports/actions.ts @@ -300,6 +300,13 @@ export { signMessage, } from '../actions/signMessage.js' +export { + type SignTransactionErrorType, + type SignTransactionParameters, + type SignTransactionReturnType, + signTransaction, +} from '../actions/signTransaction.js' + export { type SignTypedDataErrorType, type SignTypedDataParameters, diff --git a/packages/core/src/exports/index.test.ts b/packages/core/src/exports/index.test.ts index 2d6953b9cc..67fd9ad13f 100644 --- a/packages/core/src/exports/index.test.ts +++ b/packages/core/src/exports/index.test.ts @@ -55,6 +55,7 @@ test('exports', () => { "reconnect", "sendTransaction", "signMessage", + "signTransaction", "signTypedData", "simulateContract", "switchAccount", diff --git a/packages/core/src/exports/index.ts b/packages/core/src/exports/index.ts index 69dae7cc17..bf28b4115b 100644 --- a/packages/core/src/exports/index.ts +++ b/packages/core/src/exports/index.ts @@ -300,6 +300,13 @@ export { signMessage, } from '../actions/signMessage.js' +export { + type SignTransactionErrorType, + type SignTransactionParameters, + type SignTransactionReturnType, + signTransaction, +} from '../actions/signTransaction.js' + export { type SignTypedDataErrorType, type SignTypedDataParameters, diff --git a/packages/core/src/exports/query.test.ts b/packages/core/src/exports/query.test.ts index 777bfe9abe..6f6cdd4193 100644 --- a/packages/core/src/exports/query.test.ts +++ b/packages/core/src/exports/query.test.ts @@ -69,6 +69,7 @@ test('exports', () => { "reconnectMutationOptions", "sendTransactionMutationOptions", "signMessageMutationOptions", + "signTransactionMutationOptions", "signTypedDataMutationOptions", "switchAccountMutationOptions", "simulateContractQueryKey", diff --git a/packages/core/src/exports/query.ts b/packages/core/src/exports/query.ts index 5746148bab..904c26c726 100644 --- a/packages/core/src/exports/query.ts +++ b/packages/core/src/exports/query.ts @@ -312,6 +312,14 @@ export { signMessageMutationOptions, } from '../query/signMessage.js' +export { + type SignTransactionData, + type SignTransactionVariables, + type SignTransactionMutate, + type SignTransactionMutateAsync, + signTransactionMutationOptions, +} from '../query/signTransaction.js' + export { type SignTypedDataData, type SignTypedDataVariables, diff --git a/packages/core/src/query/signTransaction.test.ts b/packages/core/src/query/signTransaction.test.ts new file mode 100644 index 0000000000..77dda26f9c --- /dev/null +++ b/packages/core/src/query/signTransaction.test.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { signTransactionMutationOptions } from './signTransaction.js' + +test('default', () => { + expect(signTransactionMutationOptions(config)).toMatchInlineSnapshot(` + { + "mutationFn": [Function], + "mutationKey": [ + "signTransaction", + ], + } + `) +}) diff --git a/packages/core/src/query/signTransaction.ts b/packages/core/src/query/signTransaction.ts new file mode 100644 index 0000000000..b66b69ef4f --- /dev/null +++ b/packages/core/src/query/signTransaction.ts @@ -0,0 +1,65 @@ +import type { MutateOptions, MutationOptions } from '@tanstack/query-core' + +import { + type SignTransactionErrorType, + type SignTransactionParameters, + type SignTransactionReturnType, + signTransaction, +} from '../actions/signTransaction.js' +import type { Config } from '../createConfig.js' +import type { Compute } from '../types/utils.js' + +export function signTransactionMutationOptions( + config: config, +) { + return { + mutationFn(variables) { + return signTransaction(config, variables) + }, + mutationKey: ['signTransaction'], + } as const satisfies MutationOptions< + SignTransactionData, + SignTransactionErrorType, + SignTransactionVariables + > +} + +export type SignTransactionData = SignTransactionReturnType + +export type SignTransactionVariables< + config extends Config, + chainId extends config['chains'][number]['id'], +> = SignTransactionParameters + +export type SignTransactionMutate = < + chainId extends config['chains'][number]['id'], +>( + variables: SignTransactionVariables, + options?: + | Compute< + MutateOptions< + SignTransactionData, + SignTransactionErrorType, + Compute>, + context + > + > + | undefined, +) => void + +export type SignTransactionMutateAsync< + config extends Config, + context = unknown, +> = ( + variables: SignTransactionVariables, + options?: + | Compute< + MutateOptions< + SignTransactionData, + SignTransactionErrorType, + Compute>, + context + > + > + | undefined, +) => Promise diff --git a/packages/react/src/exports/actions.test.ts b/packages/react/src/exports/actions.test.ts index 77f805d96c..d6ad2b191b 100644 --- a/packages/react/src/exports/actions.test.ts +++ b/packages/react/src/exports/actions.test.ts @@ -55,6 +55,7 @@ test('exports', () => { "reconnect", "sendTransaction", "signMessage", + "signTransaction", "signTypedData", "simulateContract", "switchAccount", diff --git a/packages/react/src/exports/index.test.ts b/packages/react/src/exports/index.test.ts index cba2fa8d59..df98274906 100644 --- a/packages/react/src/exports/index.test.ts +++ b/packages/react/src/exports/index.test.ts @@ -52,6 +52,7 @@ test('exports', () => { "useReconnect", "useSendTransaction", "useSignMessage", + "useSignTransaction", "useSignTypedData", "useSimulateContract", "useStorageAt", diff --git a/packages/react/src/exports/index.ts b/packages/react/src/exports/index.ts index 7a08838ce4..45be996187 100644 --- a/packages/react/src/exports/index.ts +++ b/packages/react/src/exports/index.ts @@ -257,6 +257,12 @@ export { useSignMessage, } from '../hooks/useSignMessage.js' +export { + type UseSignTransactionParameters, + type UseSignTransactionReturnType, + useSignTransaction, +} from '../hooks/useSignTransaction.js' + export { type UseSignTypedDataParameters, type UseSignTypedDataReturnType, diff --git a/packages/react/src/exports/query.test.ts b/packages/react/src/exports/query.test.ts index fe4130815d..32a18e453a 100644 --- a/packages/react/src/exports/query.test.ts +++ b/packages/react/src/exports/query.test.ts @@ -69,6 +69,7 @@ test('exports', () => { "reconnectMutationOptions", "sendTransactionMutationOptions", "signMessageMutationOptions", + "signTransactionMutationOptions", "signTypedDataMutationOptions", "switchAccountMutationOptions", "simulateContractQueryKey", diff --git a/packages/react/src/hooks/useSignTransaction.test-d.ts b/packages/react/src/hooks/useSignTransaction.test-d.ts new file mode 100644 index 0000000000..62d9c2ba3e --- /dev/null +++ b/packages/react/src/hooks/useSignTransaction.test-d.ts @@ -0,0 +1,78 @@ +import type { SignTransactionErrorType } from '@wagmi/core' +import type { TransactionSerialized } from 'viem' +import { expectTypeOf, test } from 'vitest' + +import { useSignTransaction } from './useSignTransaction.js' + +const contextValue = { foo: 'bar' } as const + +test('context', () => { + const { context, data, error, signTransaction, variables } = + useSignTransaction({ + mutation: { + onMutate(variables) { + expectTypeOf(variables).toMatchTypeOf< + { chainId?: number | undefined } | undefined + >() + return contextValue + }, + onError(error, variables, context) { + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + }>() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + }>() + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + }>() + expectTypeOf(context).toEqualTypeOf() + }, + }, + }) + + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toMatchTypeOf< + { chainId?: number | undefined } | undefined + >() + expectTypeOf(context).toEqualTypeOf() + + signTransaction( + { to: '0x' }, + { + onError(error, variables, context) { + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + }>() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + }>() + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + }>() + expectTypeOf(context).toEqualTypeOf() + }, + }, + ) +}) diff --git a/packages/react/src/hooks/useSignTransaction.test.ts b/packages/react/src/hooks/useSignTransaction.test.ts new file mode 100644 index 0000000000..f14f266d75 --- /dev/null +++ b/packages/react/src/hooks/useSignTransaction.test.ts @@ -0,0 +1,34 @@ +import { connect, disconnect } from '@wagmi/core' +import { config } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { parseEther, type TransactionRequestBase } from 'viem' +import { expect, test } from 'vitest' + +import { useSignTransaction } from './useSignTransaction.js' + +const connector = config.connectors[0]! + +const base = { + from: '0x0000000000000000000000000000000000000000', + gas: 21000n, + nonce: 785, +} satisfies TransactionRequestBase + +test('default', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => useSignTransaction()) + + result.current.signTransaction({ + ...base, + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + }) + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current.data).toMatchInlineSnapshot( + '"0x02f870018203118085065e22cad982520894d2135cfb216b74109775236e36d4b433f1df507b872386f26fc1000080c080a0af0d6c8691aae5ecfe11b40f69ea580980175ce3a242b431f65c6192c5f59663a0016d0a36a9b3100da6a45d818a4261d64ad5276d07f6313e816777705e619b91"', + ) + + await disconnect(config, { connector }) +}) diff --git a/packages/react/src/hooks/useSignTransaction.ts b/packages/react/src/hooks/useSignTransaction.ts new file mode 100644 index 0000000000..3ac11fffe1 --- /dev/null +++ b/packages/react/src/hooks/useSignTransaction.ts @@ -0,0 +1,79 @@ +'use client' + +import { useMutation } from '@tanstack/react-query' +import type { + Config, + ResolvedRegister, + SignTransactionErrorType, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type SignTransactionData, + type SignTransactionMutate, + type SignTransactionMutateAsync, + type SignTransactionVariables, + signTransactionMutationOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter } from '../types/properties.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' +import { useConfig } from './useConfig.js' + +export type UseSignTransactionParameters< + config extends Config = Config, + context = unknown, +> = Compute< + ConfigParameter & { + mutation?: + | UseMutationParameters< + SignTransactionData, + SignTransactionErrorType, + SignTransactionVariables, + context + > + | undefined + } +> + +export type UseSignTransactionReturnType< + config extends Config = Config, + context = unknown, +> = Compute< + UseMutationReturnType< + SignTransactionData, + SignTransactionErrorType, + SignTransactionVariables, + context + > & { + signTransaction: SignTransactionMutate + signTransactionAsync: SignTransactionMutateAsync + } +> + +/** https://wagmi.sh/react/api/hooks/useSignTransaction */ +export function useSignTransaction< + config extends Config = ResolvedRegister['config'], + context = unknown, +>( + parameters: UseSignTransactionParameters = {}, +): UseSignTransactionReturnType { + const { mutation } = parameters + + const config = useConfig(parameters) + + const mutationOptions = signTransactionMutationOptions(config) + const { mutate, mutateAsync, ...result } = useMutation({ + ...mutation, + ...mutationOptions, + }) + + type Return = UseSignTransactionReturnType + return { + ...result, + signTransaction: mutate as Return['signTransaction'], + signTransactionAsync: mutateAsync as Return['signTransactionAsync'], + } +} diff --git a/site/.vitepress/sidebar.ts b/site/.vitepress/sidebar.ts index e08ff57314..5a1a1de3b8 100644 --- a/site/.vitepress/sidebar.ts +++ b/site/.vitepress/sidebar.ts @@ -247,6 +247,10 @@ export function getSidebar() { text: 'useSignMessage', link: '/react/api/hooks/useSignMessage', }, + { + text: 'useSignTransaction', + link: '/react/api/hooks/useSignTransaction', + }, { text: 'useSignTypedData', link: '/react/api/hooks/useSignTypedData', @@ -876,6 +880,10 @@ export function getSidebar() { text: 'signMessage', link: '/core/api/actions/signMessage', }, + { + text: 'signTransaction', + link: '/core/api/actions/signTransaction', + }, { text: 'signTypedData', link: '/core/api/actions/signTypedData', diff --git a/site/core/api/actions/signTransaction.md b/site/core/api/actions/signTransaction.md new file mode 100644 index 0000000000..80cbaa1528 --- /dev/null +++ b/site/core/api/actions/signTransaction.md @@ -0,0 +1,342 @@ + + +# signTransaction + +Action for signing a transaction. + +## Import + +```ts +import { signTransaction } from '@wagmi/core' +``` + +## Usage + +::: code-group +```ts [index.ts] +import { signTransaction } from '@wagmi/core' +import { parseEther } from 'viem' +import { config } from './config' + +const signature = await signTransaction(config, { + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), +}) + +``` +<<< @/snippets/core/config.ts[config.ts] +::: + +## Parameters + +```ts +import { type SignTransactionParameters } from '@wagmi/core' +``` + +### accessList + +`AccessList | undefined` + +The access list. + +::: code-group +```ts [index.ts] +import { signTransaction } from '@wagmi/core' +import { parseEther } from 'viem' +import { config } from './config' + +const signature = await signTransaction(config, { + accessList: [{ // [!code focus] + address: '0x1', // [!code focus] + storageKeys: ['0x1'], // [!code focus] + }], // [!code focus] + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), +}) +``` +<<< @/snippets/core/config.ts[config.ts] +::: + +### account + +`Address | Account | undefined` + +Account to use when signing a transaction. Throws if account is not found on [`connector`](#connector). + +::: code-group +```ts [index.ts] +import { signTransaction } from '@wagmi/core' +import { parseEther } from 'viem' +import { config } from './config' + +const signature = await signTransaction(config, { + account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', // [!code focus] + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), +}) +``` +<<< @/snippets/core/config.ts[config.ts] +::: + +### chainId + +`config['chains'][number]['id'] | undefined` + +Chain ID to validate against before signing the transaction. + +::: code-group +```ts [index.ts] +import { signTransaction } from '@wagmi/core' +import { mainnet } from '@wagmi/core/chains' +import { parseEther } from 'viem' +import { config } from './config' + +const signature = await signTransaction(config, { + chainId: mainnet.id, // [!code focus] + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), +}) +``` +<<< @/snippets/core/config.ts[config.ts] +::: + +### connector + +`Connector | undefined` + +- Connector to sign the transaction with. +- Defaults to current connector. + +::: code-group +```ts [index.ts] +import { getConnections, signTransaction } from '@wagmi/core' +import { parseEther } from 'viem' +import { config } from './config' + +const connections = getConnections(config) +const signature = await signTransaction(config, { + connector: connections[0]?.connector, // [!code focus] + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), +}) +``` +<<< @/snippets/core/config.ts[config.ts] +::: + +### data + +`` `0x${string}` | undefined `` + +A contract hashed method call with encoded args. + +::: code-group +```ts [index.ts] +import { signTransaction } from '@wagmi/core' +import { parseEther } from 'viem' +import { config } from './config' + +const signature = await signTransaction(config, { + data: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // [!code focus] + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), +}) +``` +<<< @/snippets/core/config.ts[config.ts] +::: + +### gas + +`bigint | undefined | null` + +Gas provided for transaction execution, or `null` to skip the prelude gas estimation. + +::: code-group +```ts [index.ts] +import { signTransaction } from '@wagmi/core' +import { parseEther, parseGwei } from 'viem' +import { config } from './config' + +const signature = await signTransaction(config, { + gas: parseGwei('20'), // [!code focus] + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), +}) +``` +<<< @/snippets/core/config.ts[config.ts] +::: + +--- + +### gasPrice + +`bigint | undefined` + +The price in wei to pay per gas. Only applies to [Legacy Transactions](https://viem.sh/docs/glossary/terms.html#legacy-transaction). + +::: code-group +```ts [index.ts] +import { signTransaction } from '@wagmi/core' +import { parseEther, parseGwei } from 'viem' +import { config } from './config' + +const signature = await signTransaction(config, { + gasPrice: parseGwei('20'), // [!code focus] + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), +}) +``` +<<< @/snippets/core/config.ts[config.ts] +::: + +### maxFeePerGas + +`bigint | undefined` + +Total fee per gas in wei, inclusive of [`maxPriorityFeePerGas`](#maxPriorityFeePerGas). Only applies to [EIP-1559 Transactions](https://viem.sh/docs/glossary/terms.html#eip-1559-transaction). + +::: code-group +```ts [index.ts] +import { signTransaction } from '@wagmi/core' +import { parseEther, parseGwei } from 'viem' +import { config } from './config' + +const signature = await signTransaction(config, { + maxFeePerGas: parseGwei('20'), // [!code focus] + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), +}) +``` +<<< @/snippets/core/config.ts[config.ts] +::: + +### maxPriorityFeePerGas + +`bigint | undefined` + +Max priority fee per gas in wei. Only applies to [EIP-1559 Transactions](https://viem.sh/docs/glossary/terms.html#eip-1559-transaction). + +::: code-group +```ts [index.ts] +import { signTransaction } from '@wagmi/core' +import { parseEther, parseGwei } from 'viem' +import { config } from './config' + +const signature = await signTransaction(config, { + maxFeePerGas: parseGwei('20'), + maxPriorityFeePerGas: parseGwei('2'), // [!code focus] + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), +}) +``` +<<< @/snippets/core/config.ts[config.ts] +::: + +--- + +### nonce + +`number` + +Unique number identifying this transaction. + +::: code-group +```ts [index.ts] +import { signTransaction } from '@wagmi/core' +import { parseEther } from 'viem' +import { config } from './config' + +const signature = await signTransaction(config, { + nonce: 123, // [!code focus] + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), +}) +``` +<<< @/snippets/core/config.ts[config.ts] +::: + +### to + +`Address` + +The transaction recipient or contract address. + +::: code-group +```ts [index.ts] +import { signTransaction } from '@wagmi/core' +import { parseEther } from 'viem' +import { config } from './config' + +const signature = await signTransaction(config, { + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', // [!code focus] + value: parseEther('0.01'), +}) +``` +<<< @/snippets/core/config.ts[config.ts] +::: + +### type + +`'legacy' | 'eip1559' | 'eip2930' | undefined` + +Optional transaction request type to narrow parameters. + +::: code-group +```ts [index.ts] +import { signTransaction } from '@wagmi/core' +import { parseEther } from 'viem' +import { config } from './config' + +const signature = await signTransaction(config, { + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + type: 'eip1559', // [!code focus] + value: parseEther('0.01'), +}) +``` +<<< @/snippets/core/config.ts[config.ts] +::: + +### value + +`bigint | undefined` + +Value in wei sent with this transaction. + +::: code-group +```ts [index.ts] +import { signTransaction } from '@wagmi/core' +import { parseEther } from 'viem' +import { config } from './config' + +const signature = await signTransaction(config, { + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), // [!code focus] +}) +``` +<<< @/snippets/core/config.ts[config.ts] +::: + +## Return Type + +```ts +import { type SignTransactionReturnType } from '@wagmi/core' +``` + +[`Hash`](https://viem.sh/docs/glossary/types.html#hash) + +Transaction hash. + +## Error + +```ts +import { type SignTransactionErrorType } from '@wagmi/core' +``` + + + +## Viem + +- [`signTransaction`](https://viem.sh/docs/actions/wallet/signTransaction.html) diff --git a/site/react/api/hooks/useSignTransaction.md b/site/react/api/hooks/useSignTransaction.md new file mode 100644 index 0000000000..312a4c662c --- /dev/null +++ b/site/react/api/hooks/useSignTransaction.md @@ -0,0 +1,93 @@ +--- +title: useSignTransaction +description: Hook for signing a transaction. +--- + + + +# useSignTransaction + +Hook for signing a transaction. + +## Import + +```ts +import { useSignTransaction } from 'wagmi' +``` + +## Usage + +::: code-group +```tsx [index.tsx] +import { useSignTransaction } from 'wagmi' +import { parseEther } from 'viem' + +function App() { + const { signTransaction } = useSignTransaction() + + return ( + + ) +} +``` +<<< @/snippets/react/config.ts[config.ts] +::: + +## Parameters + +```ts +import { type UseSignTransactionParameters } from 'wagmi' +``` + +### config + +`Config | undefined` + +[`Config`](/react/api/createConfig#config) to use instead of retrieving from the from nearest [`WagmiProvider`](/react/api/WagmiProvider). + +::: code-group +```tsx [index.tsx] +import { useSignTransaction } from 'wagmi' +import { config } from './config' // [!code focus] + +function App() { + const result = useSignTransaction({ + config, // [!code focus] + }) +} +``` +<<< @/snippets/react/config.ts[config.ts] +::: + + + +## Return Type + +```ts +import { type UseSignTransactionReturnType } from 'wagmi' +``` + + + + + +## Action + +- [`signTransaction`](/core/api/actions/signTransaction) From d49915c1e45a739a396d437ec0b80a3591b941a7 Mon Sep 17 00:00:00 2001 From: Vitor Date: Tue, 8 Oct 2024 00:08:22 -0300 Subject: [PATCH 2/4] chore: format --- packages/core/src/actions/signTransaction.test.ts | 2 +- packages/react/src/hooks/useSignTransaction.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/actions/signTransaction.test.ts b/packages/core/src/actions/signTransaction.test.ts index 2e74e25427..4e12c28e75 100644 --- a/packages/core/src/actions/signTransaction.test.ts +++ b/packages/core/src/actions/signTransaction.test.ts @@ -1,5 +1,5 @@ import { config, privateKey } from '@wagmi/test' -import { parseEther, type TransactionRequestBase } from 'viem' +import { type TransactionRequestBase, parseEther } from 'viem' import { beforeEach, expect, test } from 'vitest' import { privateKeyToAccount } from 'viem/accounts' diff --git a/packages/react/src/hooks/useSignTransaction.test.ts b/packages/react/src/hooks/useSignTransaction.test.ts index f14f266d75..1a6f6bede3 100644 --- a/packages/react/src/hooks/useSignTransaction.test.ts +++ b/packages/react/src/hooks/useSignTransaction.test.ts @@ -1,7 +1,7 @@ import { connect, disconnect } from '@wagmi/core' import { config } from '@wagmi/test' import { renderHook, waitFor } from '@wagmi/test/react' -import { parseEther, type TransactionRequestBase } from 'viem' +import { type TransactionRequestBase, parseEther } from 'viem' import { expect, test } from 'vitest' import { useSignTransaction } from './useSignTransaction.js' From cb3349445c56c3c2e170a829ac45e0454ddaa674 Mon Sep 17 00:00:00 2001 From: Vitor Date: Wed, 9 Oct 2024 14:37:12 -0300 Subject: [PATCH 3/4] feat: send raw transaction --- .../src/actions/sendRawTransaction.test.ts | 50 ++++++ .../core/src/actions/sendRawTransaction.ts | 61 ++++++++ .../core/src/actions/signTransaction.test.ts | 2 +- packages/core/src/exports/actions.test.ts | 1 + packages/core/src/exports/actions.ts | 7 + packages/core/src/exports/index.test.ts | 1 + packages/core/src/exports/index.ts | 7 + packages/core/src/exports/query.test.ts | 1 + packages/core/src/exports/query.ts | 8 + .../core/src/query/sendRawTransaction.test.ts | 15 ++ packages/core/src/query/sendRawTransaction.ts | 66 ++++++++ packages/react/src/exports/actions.test.ts | 1 + packages/react/src/exports/index.test.ts | 1 + packages/react/src/exports/index.ts | 6 + packages/react/src/exports/query.test.ts | 1 + .../src/hooks/useSendRawTransaction.test-d.ts | 80 ++++++++++ .../src/hooks/useSendRawTransaction.test.ts | 24 +++ .../react/src/hooks/useSendRawTransaction.ts | 79 ++++++++++ site/.vitepress/sidebar.ts | 8 + site/core/api/actions/sendRawTransaction.md | 144 ++++++++++++++++++ site/react/api/hooks/useSendRawTransaction.md | 92 +++++++++++ 21 files changed, 654 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/actions/sendRawTransaction.test.ts create mode 100644 packages/core/src/actions/sendRawTransaction.ts create mode 100644 packages/core/src/query/sendRawTransaction.test.ts create mode 100644 packages/core/src/query/sendRawTransaction.ts create mode 100644 packages/react/src/hooks/useSendRawTransaction.test-d.ts create mode 100644 packages/react/src/hooks/useSendRawTransaction.test.ts create mode 100644 packages/react/src/hooks/useSendRawTransaction.ts create mode 100644 site/core/api/actions/sendRawTransaction.md create mode 100644 site/react/api/hooks/useSendRawTransaction.md diff --git a/packages/core/src/actions/sendRawTransaction.test.ts b/packages/core/src/actions/sendRawTransaction.test.ts new file mode 100644 index 0000000000..86d06d41f6 --- /dev/null +++ b/packages/core/src/actions/sendRawTransaction.test.ts @@ -0,0 +1,50 @@ +import { config, transactionHashRegex } from '@wagmi/test' +import { parseEther } from 'viem' +import { beforeEach, expect, test } from 'vitest' + +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { sendRawTransaction } from './sendRawTransaction.js' +import { signTransaction } from './signTransaction.js' + +const connector = config.connectors[0]! + +beforeEach(async () => { + if (config.state.current === connector.uid) + await disconnect(config, { connector }) +}) + +test('default', async () => { + await connect(config, { connector }) + const serializedTransaction = await signTransaction(config, { + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + }) + + expect( + sendRawTransaction(config, { + serializedTransaction, + }), + ).resolves.toMatch(transactionHashRegex) +}) + +test('behavior: connector not connected', async () => { + await connect(config, { connector }) + + const serializedTransaction = await signTransaction(config, { + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + }) + + await expect( + sendRawTransaction(config, { + connector: config.connectors[1], + serializedTransaction, + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [ConnectorNotConnectedError: Connector not connected. + + Version: @wagmi/core@x.y.z] + `) + await disconnect(config, { connector }) +}) diff --git a/packages/core/src/actions/sendRawTransaction.ts b/packages/core/src/actions/sendRawTransaction.ts new file mode 100644 index 0000000000..59c5308344 --- /dev/null +++ b/packages/core/src/actions/sendRawTransaction.ts @@ -0,0 +1,61 @@ +import { + type SendRawTransactionErrorType as viem_SendRawTransactionErrorType, + type SendRawTransactionParameters as viem_SendRawTransactionParameters, + type SendRawTransactionReturnType as viem_SendRawTransactionReturnType, + sendRawTransaction as viem_sendRawTransaction, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { BaseErrorType, ErrorType } from '../errors/base.js' +import type { + ChainIdParameter, + 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 SendRawTransactionParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = Compute< + viem_SendRawTransactionParameters & + ChainIdParameter & + ConnectorParameter +> + +export type SendRawTransactionReturnType = viem_SendRawTransactionReturnType + +export type SendRawTransactionErrorType = + // getConnectorClient() + | GetConnectorClientErrorType + // base + | BaseErrorType + | ErrorType + // viem + | viem_SendRawTransactionErrorType + +/** https://wagmi.sh/core/api/actions/sendRawTransaction */ +export async function sendRawTransaction< + config extends Config, + chainId extends config['chains'][number]['id'], +>( + config: config, + parameters: SendRawTransactionParameters, +): Promise { + const { chainId, connector, ...rest } = parameters + + const client = await getConnectorClient(config, { chainId, connector }) + + const action = getAction( + client, + viem_sendRawTransaction, + 'sendRawTransaction', + ) + + return action(rest) +} diff --git a/packages/core/src/actions/signTransaction.test.ts b/packages/core/src/actions/signTransaction.test.ts index 4e12c28e75..b04b0ab3ce 100644 --- a/packages/core/src/actions/signTransaction.test.ts +++ b/packages/core/src/actions/signTransaction.test.ts @@ -24,7 +24,7 @@ test('default', async () => { await connect(config, { connector }) await expect( signTransaction(config, { - // ...base, + ...base, to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', value: parseEther('0.01'), }), diff --git a/packages/core/src/exports/actions.test.ts b/packages/core/src/exports/actions.test.ts index d6ad2b191b..6362eccd54 100644 --- a/packages/core/src/exports/actions.test.ts +++ b/packages/core/src/exports/actions.test.ts @@ -53,6 +53,7 @@ test('exports', () => { "readContract", "readContracts", "reconnect", + "sendRawTransaction", "sendTransaction", "signMessage", "signTransaction", diff --git a/packages/core/src/exports/actions.ts b/packages/core/src/exports/actions.ts index 480691e0a6..059f0be711 100644 --- a/packages/core/src/exports/actions.ts +++ b/packages/core/src/exports/actions.ts @@ -286,6 +286,13 @@ export { reconnect, } from '../actions/reconnect.js' +export { + type SendRawTransactionErrorType, + type SendRawTransactionParameters, + type SendRawTransactionReturnType, + sendRawTransaction, +} from '../actions/sendRawTransaction.js' + export { type SendTransactionErrorType, type SendTransactionParameters, diff --git a/packages/core/src/exports/index.test.ts b/packages/core/src/exports/index.test.ts index 67fd9ad13f..ef64a41425 100644 --- a/packages/core/src/exports/index.test.ts +++ b/packages/core/src/exports/index.test.ts @@ -53,6 +53,7 @@ test('exports', () => { "readContract", "readContracts", "reconnect", + "sendRawTransaction", "sendTransaction", "signMessage", "signTransaction", diff --git a/packages/core/src/exports/index.ts b/packages/core/src/exports/index.ts index bf28b4115b..1064d7b235 100644 --- a/packages/core/src/exports/index.ts +++ b/packages/core/src/exports/index.ts @@ -286,6 +286,13 @@ export { reconnect, } from '../actions/reconnect.js' +export { + type SendRawTransactionErrorType, + type SendRawTransactionParameters, + type SendRawTransactionReturnType, + sendRawTransaction, +} from '../actions/sendRawTransaction.js' + export { type SendTransactionErrorType, type SendTransactionParameters, diff --git a/packages/core/src/exports/query.test.ts b/packages/core/src/exports/query.test.ts index 6f6cdd4193..feb07eadbc 100644 --- a/packages/core/src/exports/query.test.ts +++ b/packages/core/src/exports/query.test.ts @@ -67,6 +67,7 @@ test('exports', () => { "readContractsQueryKey", "readContractsQueryOptions", "reconnectMutationOptions", + "sendRawTransactionMutationOptions", "sendTransactionMutationOptions", "signMessageMutationOptions", "signTransactionMutationOptions", diff --git a/packages/core/src/exports/query.ts b/packages/core/src/exports/query.ts index 904c26c726..bc285738c1 100644 --- a/packages/core/src/exports/query.ts +++ b/packages/core/src/exports/query.ts @@ -296,6 +296,14 @@ export { reconnectMutationOptions, } from '../query/reconnect.js' +export { + type SendRawTransactionData, + type SendRawTransactionVariables, + type SendRawTransactionMutate, + type SendRawTransactionMutateAsync, + sendRawTransactionMutationOptions, +} from '../query/sendRawTransaction.js' + export { type SendTransactionData, type SendTransactionVariables, diff --git a/packages/core/src/query/sendRawTransaction.test.ts b/packages/core/src/query/sendRawTransaction.test.ts new file mode 100644 index 0000000000..1e2f141f70 --- /dev/null +++ b/packages/core/src/query/sendRawTransaction.test.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { sendRawTransactionMutationOptions } from './sendRawTransaction.js' + +test('default', () => { + expect(sendRawTransactionMutationOptions(config)).toMatchInlineSnapshot(` + { + "mutationFn": [Function], + "mutationKey": [ + "sendRawTransaction", + ], + } + `) +}) diff --git a/packages/core/src/query/sendRawTransaction.ts b/packages/core/src/query/sendRawTransaction.ts new file mode 100644 index 0000000000..b06e81b510 --- /dev/null +++ b/packages/core/src/query/sendRawTransaction.ts @@ -0,0 +1,66 @@ +import type { MutateOptions, MutationOptions } from '@tanstack/query-core' + +import { + type SendRawTransactionErrorType, + type SendRawTransactionParameters, + type SendRawTransactionReturnType, + sendRawTransaction, +} from '../actions/sendRawTransaction.js' +import type { Config } from '../createConfig.js' +import type { Compute } from '../types/utils.js' + +export function sendRawTransactionMutationOptions( + config: config, +) { + return { + mutationFn(variables) { + return sendRawTransaction(config, variables) + }, + mutationKey: ['sendRawTransaction'], + } as const satisfies MutationOptions< + SendRawTransactionData, + SendRawTransactionErrorType, + SendRawTransactionVariables + > +} + +export type SendRawTransactionData = Compute + +export type SendRawTransactionVariables< + config extends Config, + chainId extends config['chains'][number]['id'], +> = SendRawTransactionParameters + +export type SendRawTransactionMutate< + config extends Config, + context = unknown, +> = ( + variables: SendRawTransactionVariables, + options?: + | Compute< + MutateOptions< + SendRawTransactionData, + SendRawTransactionErrorType, + Compute>, + context + > + > + | undefined, +) => void + +export type SendRawTransactionMutateAsync< + config extends Config, + context = unknown, +> = ( + variables: SendRawTransactionVariables, + options?: + | Compute< + MutateOptions< + SendRawTransactionData, + SendRawTransactionErrorType, + Compute>, + context + > + > + | undefined, +) => Promise diff --git a/packages/react/src/exports/actions.test.ts b/packages/react/src/exports/actions.test.ts index d6ad2b191b..6362eccd54 100644 --- a/packages/react/src/exports/actions.test.ts +++ b/packages/react/src/exports/actions.test.ts @@ -53,6 +53,7 @@ test('exports', () => { "readContract", "readContracts", "reconnect", + "sendRawTransaction", "sendTransaction", "signMessage", "signTransaction", diff --git a/packages/react/src/exports/index.test.ts b/packages/react/src/exports/index.test.ts index df98274906..bf0e0475b5 100644 --- a/packages/react/src/exports/index.test.ts +++ b/packages/react/src/exports/index.test.ts @@ -50,6 +50,7 @@ test('exports', () => { "useReadContracts", "useContractReads", "useReconnect", + "useSendRawTransaction", "useSendTransaction", "useSignMessage", "useSignTransaction", diff --git a/packages/react/src/exports/index.ts b/packages/react/src/exports/index.ts index 45be996187..bab3eecad4 100644 --- a/packages/react/src/exports/index.ts +++ b/packages/react/src/exports/index.ts @@ -245,6 +245,12 @@ export { useReconnect, } from '../hooks/useReconnect.js' +export { + type UseSendRawTransactionParameters, + type UseSendRawTransactionReturnType, + useSendRawTransaction, +} from '../hooks/useSendRawTransaction.js' + export { type UseSendTransactionParameters, type UseSendTransactionReturnType, diff --git a/packages/react/src/exports/query.test.ts b/packages/react/src/exports/query.test.ts index 32a18e453a..3d800b29df 100644 --- a/packages/react/src/exports/query.test.ts +++ b/packages/react/src/exports/query.test.ts @@ -67,6 +67,7 @@ test('exports', () => { "readContractsQueryKey", "readContractsQueryOptions", "reconnectMutationOptions", + "sendRawTransactionMutationOptions", "sendTransactionMutationOptions", "signMessageMutationOptions", "signTransactionMutationOptions", diff --git a/packages/react/src/hooks/useSendRawTransaction.test-d.ts b/packages/react/src/hooks/useSendRawTransaction.test-d.ts new file mode 100644 index 0000000000..fc21bc612b --- /dev/null +++ b/packages/react/src/hooks/useSendRawTransaction.test-d.ts @@ -0,0 +1,80 @@ +import type { SendRawTransactionErrorType } from '@wagmi/core' +import type { Hash } from 'viem' +import { expectTypeOf, test } from 'vitest' + +import { useSendRawTransaction } from './useSendRawTransaction.js' + +const contextValue = { foo: 'bar' } as const + +test('context', () => { + const { context, data, error, sendRawTransaction, variables } = + useSendRawTransaction({ + mutation: { + onMutate(variables) { + expectTypeOf(variables).toMatchTypeOf< + { chainId?: number | undefined } | undefined + >() + return contextValue + }, + onError(error, variables, context) { + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + }>() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + }>() + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf( + error, + ).toEqualTypeOf() + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + }>() + expectTypeOf(context).toEqualTypeOf() + }, + }, + }) + + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toMatchTypeOf< + { chainId?: number | undefined } | undefined + >() + expectTypeOf(context).toEqualTypeOf() + + sendRawTransaction( + { serializedTransaction: '0x' }, + { + onError(error, variables, context) { + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + }>() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + }>() + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + }>() + expectTypeOf(context).toEqualTypeOf() + }, + }, + ) +}) diff --git a/packages/react/src/hooks/useSendRawTransaction.test.ts b/packages/react/src/hooks/useSendRawTransaction.test.ts new file mode 100644 index 0000000000..684ea5e576 --- /dev/null +++ b/packages/react/src/hooks/useSendRawTransaction.test.ts @@ -0,0 +1,24 @@ +import { connect, disconnect } from '@wagmi/core' +import { config, transactionHashRegex } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useSendRawTransaction } from './useSendRawTransaction.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => useSendRawTransaction()) + + result.current.sendRawTransaction({ + serializedTransaction: + '0x02f870018203118085065e22cad982520894d2135cfb216b74109775236e36d4b433f1df507b872386f26fc1000080c080a0af0d6c8691aae5ecfe11b40f69ea580980175ce3a242b431f65c6192c5f59663a0016d0a36a9b3100da6a45d818a4261d64ad5276d07f6313e816777705e619b91', + }) + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current.data).toMatch(transactionHashRegex) + + await disconnect(config, { connector }) +}) diff --git a/packages/react/src/hooks/useSendRawTransaction.ts b/packages/react/src/hooks/useSendRawTransaction.ts new file mode 100644 index 0000000000..66138474c4 --- /dev/null +++ b/packages/react/src/hooks/useSendRawTransaction.ts @@ -0,0 +1,79 @@ +'use client' + +import { useMutation } from '@tanstack/react-query' +import type { + Config, + ResolvedRegister, + SendRawTransactionErrorType, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type SendRawTransactionData, + type SendRawTransactionMutate, + type SendRawTransactionMutateAsync, + type SendRawTransactionVariables, + sendRawTransactionMutationOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter } from '../types/properties.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' +import { useConfig } from './useConfig.js' + +export type UseSendRawTransactionParameters< + config extends Config = Config, + context = unknown, +> = Compute< + ConfigParameter & { + mutation?: + | UseMutationParameters< + SendRawTransactionData, + SendRawTransactionErrorType, + SendRawTransactionVariables, + context + > + | undefined + } +> + +export type UseSendRawTransactionReturnType< + config extends Config = Config, + context = unknown, +> = Compute< + UseMutationReturnType< + SendRawTransactionData, + SendRawTransactionErrorType, + SendRawTransactionVariables, + context + > & { + sendRawTransaction: SendRawTransactionMutate + sendRawTransactionAsync: SendRawTransactionMutateAsync + } +> + +/** https://wagmi.sh/react/api/hooks/useSendRawTransaction */ +export function useSendRawTransaction< + config extends Config = ResolvedRegister['config'], + context = unknown, +>( + parameters: UseSendRawTransactionParameters = {}, +): UseSendRawTransactionReturnType { + const { mutation } = parameters + + const config = useConfig(parameters) + + const mutationOptions = sendRawTransactionMutationOptions(config) + const { mutate, mutateAsync, ...result } = useMutation({ + ...mutation, + ...mutationOptions, + }) + + type Return = UseSendRawTransactionReturnType + return { + ...result, + sendRawTransaction: mutate as Return['sendRawTransaction'], + sendRawTransactionAsync: mutateAsync as Return['sendRawTransactionAsync'], + } +} diff --git a/site/.vitepress/sidebar.ts b/site/.vitepress/sidebar.ts index 5a1a1de3b8..61c2fb28e5 100644 --- a/site/.vitepress/sidebar.ts +++ b/site/.vitepress/sidebar.ts @@ -239,6 +239,10 @@ export function getSidebar() { link: '/react/api/hooks/useReadContracts', }, { text: 'useReconnect', link: '/react/api/hooks/useReconnect' }, + { + text: 'useSendRawTransaction', + link: '/react/api/hooks/useSendRawTransaction', + }, { text: 'useSendTransaction', link: '/react/api/hooks/useSendTransaction', @@ -872,6 +876,10 @@ export function getSidebar() { text: 'readContracts', link: '/core/api/actions/readContracts', }, + { + text: 'sendRawTransaction', + link: '/core/api/actions/sendRawTransaction', + }, { text: 'sendTransaction', link: '/core/api/actions/sendTransaction', diff --git a/site/core/api/actions/sendRawTransaction.md b/site/core/api/actions/sendRawTransaction.md new file mode 100644 index 0000000000..b9fa3fa252 --- /dev/null +++ b/site/core/api/actions/sendRawTransaction.md @@ -0,0 +1,144 @@ + + +# sendRawTransaction + +Action for sending a signed transaction to the network. + +## Import + +```ts +import { sendRawTransaction } from '@wagmi/core' +``` + +## Usage + +::: code-group +```ts [index.ts] +import { sendRawTransaction, signTransaction } from '@wagmi/core' +import { parseEther } from 'viem' +import { config } from './config' + +const serializedTransaction = await signTransaction(config, { + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), +}) + +const result = await sendRawTransaction(config, { + serializedTransaction +}) +``` +<<< @/snippets/core/config.ts[config.ts] +::: + +## Parameters + +```ts +import { type SendRawTransactionParameters } from '@wagmi/core' +``` + +### chainId + +`config['chains'][number]['id'] | undefined` + +Chain ID to validate against before sending transaction. + +::: code-group +```ts [index.ts] +import { sendRawTransaction, signTransaction } from '@wagmi/core' +import { mainnet } from '@wagmi/core/chains' +import { parseEther } from 'viem' +import { config } from './config' + +const serializedTransaction = await signTransaction(config, { + chainId: mainnet.id, + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), +}) + +const result = await sendRawTransaction(config, { + chainId: mainnet.id, // [!code focus] + serializedTransaction +}) +``` +<<< @/snippets/core/config.ts[config.ts] +::: + +### connector + +`Connector | undefined` + +- Connector to send the raw transaction with. +- Defaults to current connector. + +::: code-group +```ts [index.ts] +import { getConnections, sendRawTransaction, signTransaction } from '@wagmi/core' +import { parseEther } from 'viem' +import { config } from './config' + +const connections = getConnections(config) + +const serializedTransaction = await signTransaction(config, { + connector: connections[0]?.connector, + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), +}) + +const result = await sendRawTransaction(config, { + connector: connections[0]?.connector, // [!code focus] + serializedTransaction +}) + +``` +<<< @/snippets/core/config.ts[config.ts] +::: + +### serializedTransaction + +`` `0x${string}` `` + +The signed serialized transaction. + +::: code-group +```ts [index.ts] +import { sendRawTransaction, signTransaction } from '@wagmi/core' +import { parseEther } from 'viem' +import { config } from './config' + +const serializedTransaction = await signTransaction(config, { + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), +}) + +const result = await sendRawTransaction(config, { + serializedTransaction // [!code focus] +}) +``` +<<< @/snippets/core/config.ts[config.ts] +::: + +## Return Type + +```ts +import { type SendRawTransactionReturnType } from '@wagmi/core' +``` + +[`Hash`](https://viem.sh/docs/glossary/types.html#hash) + +Transaction hash. + +## Error + +```ts +import { type SendRawTransactionErrorType } from '@wagmi/core' +``` + + + +## Viem + +- [`sendRawTransaction`](https://viem.sh/docs/actions/wallet/sendRawTransaction.html) diff --git a/site/react/api/hooks/useSendRawTransaction.md b/site/react/api/hooks/useSendRawTransaction.md new file mode 100644 index 0000000000..368742e953 --- /dev/null +++ b/site/react/api/hooks/useSendRawTransaction.md @@ -0,0 +1,92 @@ +--- +title: useSendRawTransaction +description: Hook for sending a signed transaction to the network. +--- + + + +# useSendRawTransaction + +Hook for sending a signed transaction to the network. + +## Import + +```ts +import { useSendRawTransaction } from 'wagmi' +``` + +## Usage + +::: code-group +```tsx [index.tsx] +import { useSendRawTransaction } from 'wagmi' +import { parseEther } from 'viem' + +function App() { + const { sendRawTransaction } = useSendRawTransaction() + + return ( + + ) +} +``` +<<< @/snippets/react/config.ts[config.ts] +::: + +## Parameters + +```ts +import { type UseSendRawTransactionParameters } from 'wagmi' +``` + +### config + +`Config | undefined` + +[`Config`](/react/api/createConfig#config) to use instead of retrieving from the from nearest [`WagmiProvider`](/react/api/WagmiProvider). + +::: code-group +```tsx [index.tsx] +import { useSendRawTransaction } from 'wagmi' +import { config } from './config' // [!code focus] + +function App() { + const result = useSendRawTransaction({ + config, // [!code focus] + }) +} +``` +<<< @/snippets/react/config.ts[config.ts] +::: + + + +## Return Type + +```ts +import { type UseSendRawTransactionReturnType } from 'wagmi' +``` + + + + + +## Action + +- [`sendRawTransaction`](/core/api/actions/sendRawTransaction) From ce942c42864aaf4f26fbfa39a299e46942a7fbe8 Mon Sep 17 00:00:00 2001 From: Vitor Date: Wed, 9 Oct 2024 14:40:03 -0300 Subject: [PATCH 4/4] chore: changeset --- .changeset/rich-berries-punch.md | 5 +++++ .changeset/strong-eels-punch.md | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .changeset/rich-berries-punch.md create mode 100644 .changeset/strong-eels-punch.md diff --git a/.changeset/rich-berries-punch.md b/.changeset/rich-berries-punch.md new file mode 100644 index 0000000000..1dd0940143 --- /dev/null +++ b/.changeset/rich-berries-punch.md @@ -0,0 +1,5 @@ +--- +"@wagmi/core": minor +--- + +Added `sendRawTransaction` and `signTransaction` actions. diff --git a/.changeset/strong-eels-punch.md b/.changeset/strong-eels-punch.md new file mode 100644 index 0000000000..3fff6637f8 --- /dev/null +++ b/.changeset/strong-eels-punch.md @@ -0,0 +1,5 @@ +--- +"wagmi": minor +--- + +Added `useSendRawTransaction` and `useSignTransaction` hooks.