Skip to content

Commit

Permalink
feat: connector property to skip contract simulation (#3868)
Browse files Browse the repository at this point in the history
* feat: connector property to indicate skip simulation

* remove console.log

* tweak

* chore: skip on send tx

* chore: comment

* changeset

* chore: skipSimulateContract -> supportsSimulation

* chore: tweaks

* update snap
  • Loading branch information
jxom committed Apr 29, 2024
1 parent 3f36d70 commit c2af20b
Show file tree
Hide file tree
Showing 8 changed files with 30 additions and 2 deletions.
7 changes: 7 additions & 0 deletions .changeset/twelve-walls-deny.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@wagmi/connectors": minor
"@wagmi/core": minor
"wagmi": minor
---

Added `supportsSimulation` property to connectors that indicates if the connector's wallet supports contract simulation.
1 change: 1 addition & 0 deletions docs/dev/creating-connectors.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ The type error tells you what properties are missing from `createConnector`'s re
- `icon`: Optional icon URL for the connector.
- `id`: The ID for the connector. This should be camel-cased and as short as possible. Example: `fooBarBaz`.
- `name`: Human-readable name for the connector. Example: `'Foo Bar Baz'`.
- `supportsSimulation`: Whether the connector supports contract simulation. This should be disabled if a connector's wallet cannot accurately simulate contract writes or display contract revert messages. Defaults to `false`.

#### Methods

Expand Down
1 change: 1 addition & 0 deletions packages/connectors/src/coinbaseWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export function coinbaseWallet(parameters: CoinbaseWalletParameters) {
return createConnector<Provider, Properties>((config) => ({
id: 'coinbaseWalletSDK',
name: 'Coinbase Wallet',
supportsSimulation: true,
type: coinbaseWallet.type,
async connect({ chainId } = {}) {
try {
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/actions/sendTransaction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ test('behavior: value exceeds balance', async () => {
from: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
to: 0xd2135CfB216b74109775236E36d4b433F1DF507B
value: 99999 ETH
gas: 21000
Details: Insufficient funds for gas * price + value
Version: viem@2.8.4"
Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/actions/sendTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type {
} from '../types/properties.js'
import { type Evaluate } from '../types/utils.js'
import { getAction } from '../utils/getAction.js'
import { getAccount } from './getAccount.js'
import {
type GetConnectorClientErrorType,
getConnectorClient,
Expand Down Expand Up @@ -74,7 +75,15 @@ export async function sendTransaction<
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 (activeConnector?.supportsSimulation) return undefined

// Skip gas estimation if `null` is provided.
if (gas_ === null) return undefined

Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/actions/writeContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
} from '../types/properties.js'
import type { Evaluate, UnionEvaluate, UnionOmit } from '../types/utils.js'
import { getAction } from '../utils/getAction.js'
import { getAccount } from './getAccount.js'
import {
type GetConnectorClientErrorType,
getConnectorClient,
Expand Down Expand Up @@ -101,8 +102,11 @@ export async function writeContract<
else
client = await getConnectorClient(config, { account, chainId, connector })

const { connector: activeConnector } = getAccount(config)

let request
if (__mode === 'prepared') request = rest
if (__mode === 'prepared' || activeConnector?.supportsSimulation)
request = rest
else {
const { request: simulateRequest } = await simulateContract(config, {
...rest,
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/connectors/createConnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export type CreateConnectorFn<
readonly icon?: string | undefined
readonly id: string
readonly name: string
readonly supportsSimulation?: boolean | undefined
readonly type: string

setup?(): Promise<void>
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/connectors/injected.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ export type InjectedParameters = {
target?: TargetId | Target | (() => Target | undefined) | undefined
}

// Regex of wallets/providers that can accurately simulate contract calls & display contract revert reasons.
const supportsSimulationIdRegex = /(rabby|trustwallet)/

const targetMap = {
coinbaseWallet: {
id: 'coinbaseWallet',
Expand Down Expand Up @@ -141,6 +144,9 @@ export function injected(parameters: InjectedParameters = {}) {
get name() {
return getTarget().name
},
get supportsSimulation() {
return supportsSimulationIdRegex.test(this.id.toLowerCase())
},
type: injected.type,
async setup() {
const provider = await this.getProvider()
Expand Down

0 comments on commit c2af20b

Please sign in to comment.