Skip to content
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
7 changes: 7 additions & 0 deletions .changeset/nasty-dancers-hammer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"wagmi": minor
"@wagmi/core": minor
"@wagmi/vue": minor
---

Added `sendEip712Transaction` action and `useSendEip712Transaction` hooks for React and Vue.
44 changes: 44 additions & 0 deletions packages/core/src/actions/sendEip712Transaction.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { http, parseEther } from 'viem'
import { zksync } from 'viem/chains'
import { expectTypeOf, test } from 'vitest'

import { createConfig } from '../createConfig.js'
import {
type SendEip712TransactionParameters,
sendEip712Transaction,
} from './sendEip712Transaction.js'

test('chain formatters', () => {
const config = createConfig({
chains: [zksync],
transports: { [zksync.id]: http() },
})

type Result = SendEip712TransactionParameters<typeof config>
expectTypeOf<Result>().toMatchTypeOf<{
chainId?: typeof zksync.id | undefined
feeCurrency?: `0x${string}` | undefined
}>()
sendEip712Transaction(config, {
to: '0xd2135CfB216b74109775236E36d4b433F1DF507B',
value: parseEther('0.01'),
// @ts-expect-error
feeCurrency: '0x',
})

type Result2 = SendEip712TransactionParameters<
typeof config,
typeof zksync.id
>
// @ts-expect-error
expectTypeOf<Result2>().toMatchTypeOf<{
feeCurrency?: `0x${string}` | undefined
}>()
sendEip712Transaction(config, {
chainId: zksync.id,
to: '0xd2135CfB216b74109775236E36d4b433F1DF507B',
value: parseEther('0.01'),
// @ts-expect-error
feeCurrency: '0x',
})
})
105 changes: 105 additions & 0 deletions packages/core/src/actions/sendEip712Transaction.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { config, privateKey, transactionHashRegex } from '@wagmi/test'
import { parseEther } from 'viem'
import { beforeEach, expect, test } from 'vitest'

import { privateKeyToAccount } from 'viem/accounts'
import { connect } from './connect.js'
import { disconnect } from './disconnect.js'
import { sendEip712Transaction } from './sendEip712Transaction.js'

const connector = config.connectors[0]!

beforeEach(async () => {
if (config.state.current === connector.uid)
await disconnect(config, { connector })
})

test('default', async () => {
const result = await connect(config, { connector })
config.state.connections.set(connector.uid, {
...result,
// Switch up the current account because the default one is running out of funds somewhere
accounts: result.accounts.slice(1) as unknown as typeof result.accounts,
connector,
})
await expect(
sendEip712Transaction(config, {
to: '0xd2135CfB216b74109775236E36d4b433F1DF507B',
value: parseEther('0.0001'),
}),
).resolves.toMatch(transactionHashRegex)
await disconnect(config, { connector })
})

test('behavior: connector not connected', async () => {
await connect(config, { connector })
await expect(
sendEip712Transaction(config, {
connector: config.connectors[1],
to: '0xd2135CfB216b74109775236E36d4b433F1DF507B',
value: parseEther('0.0001'),
}),
).rejects.toThrowErrorMatchingInlineSnapshot(`
[ConnectorNotConnectedError: Connector not connected.

Version: @wagmi/[email protected]]
`)
await disconnect(config, { connector })
})

test('behavior: account does not exist on connector', async () => {
await connect(config, { connector })
await expect(
sendEip712Transaction(config, {
account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
to: '0xd2135CfB216b74109775236E36d4b433F1DF507B',
value: parseEther('0.0001'),
}),
).rejects.toThrowErrorMatchingInlineSnapshot(`
[ConnectorAccountNotFoundError: Account "0xA0Cf798816D4b9b9866b5330EEa46a18382f251e" not found for connector "Mock Connector".

Version: @wagmi/[email protected]]
`)
await disconnect(config, { connector })
})

test('behavior: value exceeds balance', async () => {
await connect(config, { connector })
await expect(
sendEip712Transaction(config, {
to: '0xd2135CfB216b74109775236E36d4b433F1DF507B',
value: parseEther('99999'),
}),
).rejects.toThrowErrorMatchingInlineSnapshot(`
[TransactionExecutionError: The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account.

This error could arise when the account does not have enough funds to:
- pay for the total gas fee,
- pay for the value to send.

The cost of the transaction is calculated as \`gas * gas fee + value\`, where:
- \`gas\` is the amount of gas needed for transaction to execute,
- \`gas fee\` is the gas fee,
- \`value\` is the amount of ether to send to the recipient.

Request Arguments:
from: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
to: 0xd2135CfB216b74109775236E36d4b433F1DF507B
value: 99999 ETH

Details: Insufficient funds for gas * price + value
Version: 2.21.28]
`)
await disconnect(config, { connector })
})

test('behavior: local account', async () => {
const account = privateKeyToAccount(privateKey)
await expect(
sendEip712Transaction(config, {
account,
to: '0xd2135CfB216b74109775236E36d4b433F1DF507B',
value: parseEther('0.000001'),
}),
).resolves.toMatch(transactionHashRegex)
})
83 changes: 83 additions & 0 deletions packages/core/src/actions/sendEip712Transaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import type { Account, Client, TransactionRequest } from 'viem'
import {
type ChainEIP712,
type SendTransactionErrorType as viem_SendTransactionErrorType,
type SendTransactionParameters as viem_SendTransactionParameters,
type SendTransactionReturnType as viem_SendTransactionReturnType,
sendTransaction as viem_sendTransaction,
} from 'viem/zksync'
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 {
type GetConnectorClientErrorType,
getConnectorClient,
} from './getConnectorClient.js'

export type SendEip712TransactionParameters<
config extends Config = Config,
chainId extends
config['chains'][number]['id'] = config['chains'][number]['id'],
///
chains extends readonly ChainEIP712[] = SelectChains<config, chainId>,
> = {
[key in keyof chains]: Compute<
Omit<
viem_SendTransactionParameters<chains[key], Account, chains[key]>,
'chain' | 'gas'
> &
ChainIdParameter<config, chainId> &
ConnectorParameter
>
}[number] & {
/** Gas provided for transaction execution. */
gas?: TransactionRequest['gas'] | null
}

export type SendEip712TransactionReturnType = viem_SendTransactionReturnType

export type SendEip712TransactionErrorType =
// getConnectorClient()
| GetConnectorClientErrorType
// base
| BaseErrorType
| ErrorType
// viem
| viem_SendTransactionErrorType

/** https://wagmi.sh/core/api/actions/sendEip712Transaction */
export async function sendEip712Transaction<
config extends Config,
chainId extends config['chains'][number]['id'],
>(
config: config,
parameters: SendEip712TransactionParameters<config, chainId>,
): Promise<SendEip712TransactionReturnType> {
const { account, chainId, connector, ...rest } = parameters

let client: Client
if (typeof account === 'object' && account?.type === 'local')
client = config.getClient({ chainId })
else
client = await getConnectorClient(config, {
account: account ?? undefined,
chainId,
connector,
})

const action = getAction(client, viem_sendTransaction, 'sendTransaction')
const hash = await action({
...(rest as any),
...(account ? { account } : {}),
chain: chainId ? { id: chainId } : undefined,
gas: rest.gas ?? undefined,
})

return hash
}
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 @@ -53,6 +53,7 @@ test('exports', () => {
"readContract",
"readContracts",
"reconnect",
"sendEip712Transaction",
"sendTransaction",
"signMessage",
"signTypedData",
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 @@ -286,6 +286,13 @@ export {
reconnect,
} from '../actions/reconnect.js'

export {
type SendEip712TransactionErrorType,
type SendEip712TransactionParameters,
type SendEip712TransactionReturnType,
sendEip712Transaction,
} from '../actions/sendEip712Transaction.js'

export {
type SendTransactionErrorType,
type SendTransactionParameters,
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 @@ -53,6 +53,7 @@ test('exports', () => {
"readContract",
"readContracts",
"reconnect",
"sendEip712Transaction",
"sendTransaction",
"signMessage",
"signTypedData",
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 @@ -286,6 +286,13 @@ export {
reconnect,
} from '../actions/reconnect.js'

export {
type SendEip712TransactionErrorType,
type SendEip712TransactionParameters,
type SendEip712TransactionReturnType,
sendEip712Transaction,
} from '../actions/sendEip712Transaction.js'

export {
type SendTransactionErrorType,
type SendTransactionParameters,
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 @@ -67,6 +67,7 @@ test('exports', () => {
"readContractsQueryKey",
"readContractsQueryOptions",
"reconnectMutationOptions",
"sendEip712TransactionMutationOptions",
"sendTransactionMutationOptions",
"signMessageMutationOptions",
"signTypedDataMutationOptions",
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 @@ -296,6 +296,14 @@ export {
reconnectMutationOptions,
} from '../query/reconnect.js'

export {
type SendEip712TransactionData,
type SendEip712TransactionVariables,
type SendEip712TransactionMutate,
type SendEip712TransactionMutateAsync,
sendEip712TransactionMutationOptions,
} from '../query/sendEip712Transaction.js'

export {
type SendTransactionData,
type SendTransactionVariables,
Expand Down
15 changes: 15 additions & 0 deletions packages/core/src/query/sendEip712Transaction.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 { sendEip712TransactionMutationOptions } from './sendEip712Transaction.js'

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

import {
type SendEip712TransactionErrorType,
type SendEip712TransactionParameters,
type SendEip712TransactionReturnType,
sendEip712Transaction,
} from '../actions/sendEip712Transaction.js'
import type { Config } from '../createConfig.js'
import type { Compute } from '../types/utils.js'

export function sendEip712TransactionMutationOptions<config extends Config>(
config: config,
) {
return {
mutationFn(variables) {
return sendEip712Transaction(config, variables)
},
mutationKey: ['sendEip712Transaction'],
} as const satisfies MutationOptions<
SendEip712TransactionData,
SendEip712TransactionErrorType,
SendEip712TransactionVariables<config, config['chains'][number]['id']>
>
}

export type SendEip712TransactionData = Compute<SendEip712TransactionReturnType>

export type SendEip712TransactionVariables<
config extends Config,
chainId extends config['chains'][number]['id'],
> = SendEip712TransactionParameters<config, chainId>

export type SendEip712TransactionMutate<
config extends Config,
context = unknown,
> = <chainId extends config['chains'][number]['id']>(
variables: SendEip712TransactionVariables<config, chainId>,
options?:
| Compute<
MutateOptions<
SendEip712TransactionData,
SendEip712TransactionErrorType,
Compute<SendEip712TransactionVariables<config, chainId>>,
context
>
>
| undefined,
) => void

export type SendEip712TransactionMutateAsync<
config extends Config,
context = unknown,
> = <chainId extends config['chains'][number]['id']>(
variables: SendEip712TransactionVariables<config, chainId>,
options?:
| Compute<
MutateOptions<
SendEip712TransactionData,
SendEip712TransactionErrorType,
Compute<SendEip712TransactionVariables<config, chainId>>,
context
>
>
| undefined,
) => Promise<SendEip712TransactionData>
Loading