Skip to content

Commit

Permalink
feat: narrow return types for actions that include blockTag or `inc…
Browse files Browse the repository at this point in the history
…ludeTransactions` (#847)

* feat: narrow return types for actions that use  or

* tests: fix

* refactor: default cases

* refactor: include transactions on block type

* feat: support pending on formatted blocks/txns

* feat: add missing decorator generics

* refactor

* refactor

* chore: changesets

* Update brown-sheep-visit.md

* Update wicked-bananas-cover.md
  • Loading branch information
jxom authored Jul 30, 2023
1 parent 9d445f9 commit 1e5d454
Show file tree
Hide file tree
Showing 36 changed files with 1,119 additions and 196 deletions.
41 changes: 41 additions & 0 deletions .changeset/bright-jobs-boil.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
"viem": minor
---

Narrowed `getBlock`, `watchBlocks`, `getFilterChanges`, `getFilterLogs` & `getLogs` return types for when `blockTag` or `includeTransactions` is provided.

- When `blockTag !== 'pending'`, the return type will now include some non-nullish properties if it were dependent on pending blocks. Example: For `getBlock`, the `block.number` type is now non-nullish since `blockTag !== 'pending'`.
- On the other hand, when `blockTag: 'pending'`, some properties will be nullish. Example: For `getBlock`, the `block.number` type is now `null` since `blockTag === 'pending'`.
- When `includeTransactions` is provided, the return type of will narrow the `transactions` property type. Example: `block.transactions` will be `Transaction[]` when `includeTransactions: true` instead of `Hash[] | Transaction[]`.

TLDR;

```ts
// Before
const block = publicClient.getBlock({ includeTransactions: true })
block.transactions
// ^? Hash[] | Transaction[]
block.transactions[0].blockNumber
// ^? bigint | null

// After
const block = publicClient.getBlock({ includeTransactions: true })
block.transactions
// ^? Transaction[]
block.transactions[0].blockNumber
// ^? bigint

// Before
const block = publicClient.getBlock({ blockTag: 'pending', includeTransactions: true })
block.number
// ^? number | null
block.transactions[0].blockNumber
// ^? bigint | null

// After
const block = publicClient.getBlock({ blockTag: 'pending', includeTransactions: true })
block.number
// ^? null
block.transactions[0].blockNumber
// ^? null
```
19 changes: 19 additions & 0 deletions .changeset/brown-sheep-visit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
"viem": minor
---

**Type Change**: `TPending` has been added to slot 2 of the `Log` generics.

```diff
type Log<
TQuantity = bigint,
TIndex = number,
+ TPending extends boolean = boolean,
TAbiEvent extends AbiEvent | undefined = undefined,
TStrict extends boolean | undefined = undefined,
TAbi extends Abi | readonly unknown[] = [TAbiEvent],
TEventName extends string | undefined = TAbiEvent extends AbiEvent
? TAbiEvent['name']
: undefined,
>
```
18 changes: 18 additions & 0 deletions .changeset/wicked-bananas-cover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
"viem": minor
---

**Type Change**: `TIncludeTransactions` & `TBlockTag` has been added to slot 1 & 2 of the `Block` generics.

```diff
type Block<
TQuantity = bigint,
+ TIncludeTransactions extends boolean = boolean,
+ TBlockTag extends BlockTag = BlockTag,
TTransaction = Transaction<
bigint,
number,
TBlockTag extends 'pending' ? true : false
>,
>
```
26 changes: 26 additions & 0 deletions src/actions/public/createContractEventFilter.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { usdcContractConfig } from '../../_test/abis.js'
import { publicClient } from '../../_test/utils.js'
import { createContractEventFilter } from './createContractEventFilter.js'
import { expectTypeOf, test } from 'vitest'

test('fromBlock/toBlock', async () => {
const filter = await createContractEventFilter(publicClient, {
abi: usdcContractConfig.abi,
})
expectTypeOf(filter.fromBlock).toBeUndefined()
expectTypeOf(filter.toBlock).toBeUndefined()

const filter_fromBlock = await createContractEventFilter(publicClient, {
abi: usdcContractConfig.abi,
fromBlock: 69n,
})
expectTypeOf(filter_fromBlock.fromBlock).toMatchTypeOf<69n | undefined>()
expectTypeOf(filter_fromBlock.toBlock).toBeUndefined()

const filter_toBlock = await createContractEventFilter(publicClient, {
abi: usdcContractConfig.abi,
toBlock: 69n,
})
expectTypeOf(filter_toBlock.toBlock).toMatchTypeOf<69n | undefined>()
expectTypeOf(filter_toBlock.fromBlock).toBeUndefined()
})
37 changes: 30 additions & 7 deletions src/actions/public/createContractEventFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
MaybeExtractEventArgsFromAbi,
} from '../../types/contract.js'
import type { Filter } from '../../types/filter.js'
import type { Hex } from '../../types/misc.js'
import {
type EncodeEventTopicsParameters,
encodeEventTopics,
Expand All @@ -23,17 +24,19 @@ export type CreateContractEventFilterParameters<
| MaybeExtractEventArgsFromAbi<TAbi, TEventName>
| undefined = undefined,
TStrict extends boolean | undefined = undefined,
TFromBlock extends BlockNumber | BlockTag | undefined = undefined,
TToBlock extends BlockNumber | BlockTag | undefined = undefined,
> = {
address?: Address | Address[]
abi: Narrow<TAbi>
eventName?: InferEventName<TAbi, TEventName>
fromBlock?: BlockNumber | BlockTag
fromBlock?: TFromBlock | BlockNumber | BlockTag
/**
* Whether or not the logs must match the indexed/non-indexed arguments in the event ABI item.
* @default false
*/
strict?: TStrict
toBlock?: BlockNumber | BlockTag
toBlock?: TToBlock | BlockNumber | BlockTag
} & (undefined extends TEventName
? {
args?: never
Expand All @@ -56,7 +59,9 @@ export type CreateContractEventFilterReturnType<
| MaybeExtractEventArgsFromAbi<TAbi, TEventName>
| undefined = undefined,
TStrict extends boolean | undefined = undefined,
> = Filter<'event', TAbi, TEventName, TArgs, TStrict>
TFromBlock extends BlockNumber | BlockTag | undefined = undefined,
TToBlock extends BlockNumber | BlockTag | undefined = undefined,
> = Filter<'event', TAbi, TEventName, TArgs, TStrict, TFromBlock, TToBlock>

/**
* Creates a Filter to retrieve event logs that can be used with [`getFilterChanges`](https://viem.sh/docs/actions/public/getFilterChanges.html) or [`getFilterLogs`](https://viem.sh/docs/actions/public/getFilterLogs.html).
Expand Down Expand Up @@ -86,6 +91,8 @@ export async function createContractEventFilter<
TEventName extends string | undefined,
TArgs extends MaybeExtractEventArgsFromAbi<TAbi, TEventName> | undefined,
TStrict extends boolean | undefined = undefined,
TFromBlock extends BlockNumber | BlockTag | undefined = undefined,
TToBlock extends BlockNumber | BlockTag | undefined = undefined,
>(
client: Client<Transport, TChain>,
{
Expand All @@ -96,9 +103,23 @@ export async function createContractEventFilter<
fromBlock,
strict,
toBlock,
}: CreateContractEventFilterParameters<TAbi, TEventName, TArgs, TStrict>,
}: CreateContractEventFilterParameters<
TAbi,
TEventName,
TArgs,
TStrict,
TFromBlock,
TToBlock
>,
): Promise<
CreateContractEventFilterReturnType<TAbi, TEventName, TArgs, TStrict>
CreateContractEventFilterReturnType<
TAbi,
TEventName,
TArgs,
TStrict,
TFromBlock,
TToBlock
>
> {
const getRequest = createFilterRequestScope(client, {
method: 'eth_newFilter',
Expand All @@ -111,7 +132,7 @@ export async function createContractEventFilter<
eventName,
} as unknown as EncodeEventTopicsParameters)
: undefined
const id = await client.request({
const id: Hex = await client.request({
method: 'eth_newFilter',
params: [
{
Expand All @@ -136,6 +157,8 @@ export async function createContractEventFilter<
TAbi,
TEventName,
TArgs,
TStrict
TStrict,
TFromBlock,
TToBlock
>
}
21 changes: 21 additions & 0 deletions src/actions/public/createEventFilter.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { publicClient } from '../../_test/utils.js'
import { createEventFilter } from './createEventFilter.js'
import { expectTypeOf, test } from 'vitest'

test('fromBlock/toBlock', async () => {
const filter = await createEventFilter(publicClient)
expectTypeOf(filter.fromBlock).toBeUndefined()
expectTypeOf(filter.toBlock).toBeUndefined()

const filter_fromBlock = await createEventFilter(publicClient, {
fromBlock: 69n,
})
expectTypeOf(filter_fromBlock.fromBlock).toMatchTypeOf<69n | undefined>()
expectTypeOf(filter_fromBlock.toBlock).toBeUndefined()

const filter_toBlock = await createEventFilter(publicClient, {
toBlock: 69n,
})
expectTypeOf(filter_toBlock.toBlock).toMatchTypeOf<69n | undefined>()
expectTypeOf(filter_toBlock.fromBlock).toBeUndefined()
})
34 changes: 28 additions & 6 deletions src/actions/public/createEventFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type {
MaybeExtractEventArgsFromAbi,
} from '../../types/contract.js'
import type { Filter } from '../../types/filter.js'
import type { LogTopic } from '../../types/misc.js'
import type { Hex, LogTopic } from '../../types/misc.js'
import type { Prettify } from '../../types/utils.js'
import {
type EncodeEventTopicsParameters,
Expand All @@ -21,15 +21,17 @@ import { createFilterRequestScope } from '../../utils/filters/createFilterReques
export type CreateEventFilterParameters<
TAbiEvent extends AbiEvent | undefined = undefined,
TStrict extends boolean | undefined = undefined,
TFromBlock extends BlockNumber | BlockTag | undefined = undefined,
TToBlock extends BlockNumber | BlockTag | undefined = undefined,
_Abi extends Abi | readonly unknown[] = [TAbiEvent],
_EventName extends string | undefined = MaybeAbiEventName<TAbiEvent>,
_Args extends
| MaybeExtractEventArgsFromAbi<_Abi, _EventName>
| undefined = undefined,
> = {
address?: Address | Address[]
fromBlock?: BlockNumber | BlockTag
toBlock?: BlockNumber | BlockTag
fromBlock?: TFromBlock | BlockNumber | BlockTag
toBlock?: TToBlock | BlockNumber | BlockTag
} & (MaybeExtractEventArgsFromAbi<
_Abi,
_EventName
Expand Down Expand Up @@ -69,12 +71,16 @@ export type CreateEventFilterParameters<
export type CreateEventFilterReturnType<
TAbiEvent extends AbiEvent | undefined = undefined,
TStrict extends boolean | undefined = undefined,
TFromBlock extends BlockNumber | BlockTag | undefined = undefined,
TToBlock extends BlockNumber | BlockTag | undefined = undefined,
_Abi extends Abi | readonly unknown[] = [TAbiEvent],
_EventName extends string | undefined = MaybeAbiEventName<TAbiEvent>,
_Args extends
| MaybeExtractEventArgsFromAbi<_Abi, _EventName>
| undefined = undefined,
> = Prettify<Filter<'event', _Abi, _EventName, _Args, TStrict>>
> = Prettify<
Filter<'event', _Abi, _EventName, _Args, TStrict, TFromBlock, TToBlock>
>

/**
* Creates a [`Filter`](https://viem.sh/docs/glossary/types.html#filter) to listen for new events that can be used with [`getFilterChanges`](https://viem.sh/docs/actions/public/getFilterChanges.html).
Expand Down Expand Up @@ -103,6 +109,8 @@ export async function createEventFilter<
TChain extends Chain | undefined,
TAbiEvent extends AbiEvent | undefined,
TStrict extends boolean | undefined = undefined,
TFromBlock extends BlockNumber<bigint> | BlockTag | undefined = undefined,
TToBlock extends BlockNumber<bigint> | BlockTag | undefined = undefined,
_Abi extends Abi | readonly unknown[] = [TAbiEvent],
_EventName extends string | undefined = MaybeAbiEventName<TAbiEvent>,
_Args extends
Expand All @@ -120,12 +128,22 @@ export async function createEventFilter<
}: CreateEventFilterParameters<
TAbiEvent,
TStrict,
TFromBlock,
TToBlock,
_Abi,
_EventName,
_Args
> = {} as any,
): Promise<
CreateEventFilterReturnType<TAbiEvent, TStrict, _Abi, _EventName, _Args>
CreateEventFilterReturnType<
TAbiEvent,
TStrict,
TFromBlock,
TToBlock,
_Abi,
_EventName,
_Args
>
> {
const getRequest = createFilterRequestScope(client, {
method: 'eth_newFilter',
Expand All @@ -139,7 +157,7 @@ export async function createEventFilter<
args,
} as EncodeEventTopicsParameters)

const id = await client.request({
const id: Hex = await client.request({
method: 'eth_newFilter',
params: [
{
Expand All @@ -156,13 +174,17 @@ export async function createEventFilter<
abi: event ? [event] : undefined,
args,
eventName: event ? (event as AbiEvent).name : undefined,
fromBlock,
id,
request: getRequest(id),
strict,
toBlock,
type: 'event',
} as unknown as CreateEventFilterReturnType<
TAbiEvent,
TStrict,
TFromBlock,
TToBlock,
_Abi,
_EventName,
_Args
Expand Down
Loading

1 comment on commit 1e5d454

@vercel
Copy link

@vercel vercel bot commented on 1e5d454 Jul 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.