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

fixes #2531 value type on function overload #2561

Merged
merged 14 commits into from
Aug 6, 2024
5 changes: 5 additions & 0 deletions .changeset/new-hotels-sell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"viem": patch
---

Fixed #2560: infer the correct payable `value` type on function overloads by matching function against `args`.
34 changes: 34 additions & 0 deletions src/actions/public/simulateContract.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,37 @@ test('chain formatters', async () => {
`0x${string}` | undefined
>()
})

test('https://github.com/wevm/viem/issues/2531', async () => {
const abi = parseAbi([
'function safeTransferFrom(address, address, uint256)',
'function safeTransferFrom(address, address, uint256, bytes) payable',
])

const res1 = await simulateContract(client, {
address: '0x',
abi,
functionName: 'safeTransferFrom',
args: ['0x', '0x', 123n],
// @ts-expect-error
value: 123n,
})
assertType<void>(res1.result)
expectTypeOf(res1.request.abi).toEqualTypeOf(
parseAbi(['function safeTransferFrom(address, address, uint256)']),
)

const res2 = await simulateContract(client, {
address: '0x',
abi,
functionName: 'safeTransferFrom',
args: ['0x', '0x', 123n, '0x'],
value: 123n,
})
assertType<void>(res2.result)
expectTypeOf(res2.request.abi).toEqualTypeOf(
parseAbi([
'function safeTransferFrom(address, address, uint256, bytes) payable',
]),
)
})
43 changes: 38 additions & 5 deletions src/actions/public/simulateContract.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Abi, Address } from 'abitype'
import type { Abi, AbiFunction, AbiStateMutability, Address } from 'abitype'

import {
type ParseAccountErrorType,
Expand All @@ -16,10 +16,15 @@ import type {
ContractFunctionParameters,
ContractFunctionReturnType,
ExtractAbiFunctionForArgs,
GetValue,
} from '../../types/contract.js'
import type { Hex } from '../../types/misc.js'
import type { Prettify, UnionEvaluate, UnionOmit } from '../../types/utils.js'
import type {
IsNarrowable,
NoInfer,
Prettify,
UnionEvaluate,
UnionOmit,
} from '../../types/utils.js'
import {
type DecodeFunctionResultErrorType,
decodeFunctionResult,
Expand All @@ -34,9 +39,35 @@ import {
} from '../../utils/errors/getContractError.js'
import type { WriteContractParameters } from '../wallet/writeContract.js'

import type { TransactionRequest } from '../../types/transaction.js'
import { getAction } from '../../utils/getAction.js'
import { type CallErrorType, type CallParameters, call } from './call.js'

export type GetMutabilityAwareValue<
abi extends Abi | readonly unknown[],
mutability extends AbiStateMutability = AbiStateMutability,
functionName extends ContractFunctionName<
abi,
mutability
> = ContractFunctionName<abi, mutability>,
valueType = TransactionRequest['value'],
args extends ContractFunctionArgs<
abi,
mutability,
functionName
> = ContractFunctionArgs<abi, mutability, functionName>,
abiFunction extends AbiFunction = abi extends Abi
? ExtractAbiFunctionForArgs<abi, mutability, functionName, args>
: AbiFunction,
_Narrowable extends boolean = IsNarrowable<abi, Abi>,
> = _Narrowable extends true
? abiFunction['stateMutability'] extends 'payable'
? { value?: NoInfer<valueType> | undefined }
: abiFunction['payable'] extends true
? { value?: NoInfer<valueType> | undefined }
: { value?: undefined }
: { value?: NoInfer<valueType> | undefined }

export type SimulateContractParameters<
abi extends Abi | readonly unknown[] = Abi,
functionName extends ContractFunctionName<
Expand Down Expand Up @@ -75,12 +106,14 @@ export type SimulateContractParameters<
| 'factoryData'
| 'value'
> &
GetValue<
GetMutabilityAwareValue<
abi,
'nonpayable' | 'payable',
functionName,
CallParameters<derivedChain> extends CallParameters
? CallParameters<derivedChain>['value']
: CallParameters['value']
: CallParameters['value'],
args
>

export type SimulateContractReturnType<
Expand Down
8 changes: 5 additions & 3 deletions src/actions/wallet/writeContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import type {
ContractFunctionArgs,
ContractFunctionName,
ContractFunctionParameters,
GetValue,
} from '../../types/contract.js'
import type { Hex } from '../../types/misc.js'
import type { Prettify, UnionEvaluate, UnionOmit } from '../../types/utils.js'
Expand All @@ -25,6 +24,7 @@ import {
} from '../../utils/abi/encodeFunctionData.js'
import type { FormattedTransactionRequest } from '../../utils/formatters/transactionRequest.js'
import { getAction } from '../../utils/getAction.js'
import type { GetMutabilityAwareValue } from '../public/simulateContract.js'
import {
type SendTransactionErrorType,
type SendTransactionReturnType,
Expand Down Expand Up @@ -59,10 +59,12 @@ export type WriteContractParameters<
GetChainParameter<chain, chainOverride> &
Prettify<
GetAccountParameter<account> &
GetValue<
GetMutabilityAwareValue<
abi,
'nonpayable' | 'payable',
functionName,
FormattedTransactionRequest<derivedChain>['value']
FormattedTransactionRequest<derivedChain>['value'],
args
> & {
/** Data to append to the end of the calldata. Useful for adding a ["domain" tag](https://opensea.notion.site/opensea/Seaport-Order-Attributions-ec2d69bf455041a5baa490941aad307f). */
dataSuffix?: Hex | undefined
Expand Down
Loading