Skip to content

Commit

Permalink
feat: getLogs (#44)
Browse files Browse the repository at this point in the history
* feat: getLogs

* chore: changeset

* feat: export getLogs

* docs: getLogs

* docs: getLogs types and sample values

* feat: update getLogs API to use event and args, define inline in eip1193

* docs: new getLogs API

* test: getLogs

* docs: fix arg

* tests: add another test case

---------

Co-authored-by: moxey.eth <jakemoxey@gmail.com>
  • Loading branch information
0xOlias and jxom committed Feb 3, 2023
1 parent ac69d16 commit f908190
Show file tree
Hide file tree
Showing 16 changed files with 416 additions and 12 deletions.
5 changes: 5 additions & 0 deletions .changeset/khaki-bees-play.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"viem": patch
---

Added `getLogs` action.
4 changes: 4 additions & 0 deletions site/.vitepress/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ export const sidebar: DefaultTheme.Sidebar = {
text: 'getFilterLogs',
link: '/docs/actions/public/getFilterLogs',
},
{
text: 'getLogs',
link: '/docs/actions/public/getLogs',
},
{
text: 'watchEvents',
link: '/docs/actions/public/watchEvents',
Expand Down
2 changes: 1 addition & 1 deletion site/docs/actions/public/createEventFilter.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Creates a Filter to listen for new events that can be used with [`getFilterChanges`](/docs/actions/public/getFilterChanges).

## Install
## Import

```ts
import { createEventFilter } from 'viem'
Expand Down
193 changes: 193 additions & 0 deletions site/docs/actions/public/getLogs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# getLogs

Returns a list of **event** logs matching the provided parameters.

## Import

```ts
import { getLogs } from 'viem'
```

## Usage

By default, `getLogs` returns all events. In practice, you must use scoping to filter for specific events.

```ts
import { getLogs } from 'viem'
import { publicClient } from '.'

const logs = await getLogs(publicClient) // [!code focus:99]
// [{ ... }, { ... }, { ... }]
```

## Scoping

You can also scope to a set of given attributes.

```ts
import { getLogs } from 'viem'
import { publicClient } from '.'

const logs = await getLogs(publicClient, { // [!code focus:99]
address: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
event: 'Transfer(address indexed from, address indexed to, uint256 value)',
args: {
from: '0xd8da6bf26964af9d7eed9e03e53415d37aa96045',
to: '0xa5cc3c03994db5b0d9a5eedd10cabab0813678ac'
},
fromBlock: 16330000n,
toBlock: 16330050n
})
```

### Address

A Filter can be scoped to an **address**:

```ts
const filter = await getLogs(publicClient, {
address: '0xfba3912ca04dd458c843e2ee08967fc04f3579c2'
})
```

### Event

A Filter can be scoped to an **event**:

```ts
const filter = await getLogs(publicClient, {
address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
event: 'Transfer(address indexed from, address indexed to, uint256 value)',
})
```

### Arguments

A Filter can be scoped to given **_indexed_ arguments**:

```ts
const filter = await getLogs(publicClient, {
address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
event: 'Transfer(address indexed from, address indexed to, uint256 value)',
args: {
from: '0xd8da6bf26964af9d7eed9e03e53415d37aa96045',
to: '0xa5cc3c03994db5b0d9a5eedd10cabab0813678ac'
}
})
```

Only indexed arguments in `event` are candidates for `args`.

A Filter Argument can also be an array to indicate that other values can exist in the position:

```ts
const filter = await getLogs(publicClient, {
address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
event: 'Transfer(address indexed from, address indexed to, uint256 value)',
args: {
// '0xd8da...' OR '0xa5cc...' OR '0xa152...'
from: [
'0xd8da6bf26964af9d7eed9e03e53415d37aa96045',
'0xa5cc3c03994db5b0d9a5eedd10cabab0813678ac',
'0xa152f8bb749c55e9943a3a0a3111d18ee2b3f94e',
],
}
})
```

### Block Range

A Filter can be scoped to a **block range**:

```ts
const filter = await getLogs(publicClient, {
address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
event: 'Transfer(address indexed from, address indexed to, uint256 value)',
fromBlock: 16330000n,
toBlock: 16330050n
})
```

## Returns

[`Log[]`](/docs/glossary/types#TODO)

A list of event logs.

## Parameters

### address

- **Type:** [`Address | Address[]`](/docs/glossary/types#TODO)

A contract address or a list of contract addresses. Only logs originating from the contract(s) will be included in the result.

```ts
const logs = await getLogs(publicClient, {
address: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // [!code focus]
})
```

### event

- **Type:** `string`

The event definition.

```ts
const logs = await getLogs(publicClient, {
event: 'Transfer(address indexed from, address indexed to, uint256 value)', // [!code focus]
})
```

### args

- **Type:** `EventFilterArgs`

A list of _indexed_ event arguments.

```ts
const logs = await getLogs(publicClient, {
event: 'Transfer(address indexed from, address indexed to, uint256 value)',
args: { // [!code focus:4]
from: '0xd8da6bf26964af9d7eed9e03e53415d37aa96045',
to: '0xa5cc3c03994db5b0d9a5eedd10cabab0813678ac'
},
})
```

### fromBlock

- **Type:** `bigint | 'latest' | 'earliest' | 'pending' | 'safe' | 'finalized'`

Block to start including logs from. Mutually exclusive with `blockHash`.

```ts
const filter = await createEventFilter(publicClient, {
fromBlock: 69420n // [!code focus]
})
```

### toBlock

- **Type:** `bigint | 'latest' | 'earliest' | 'pending' | 'safe' | 'finalized'`

Block to stop including logs from. Mutually exclusive with `blockHash`.

```ts
const filter = await createEventFilter(publicClient, {
toBlock: 70120n // [!code focus]
})
```

### blockHash

- **Type:** `'0x${string}'`

Block hash to include logs from. Mutually exclusive with `fromBlock`/`toBlock`.

```ts
const logs = await getLogs(publicClient, {
blockHash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d' // [!code focus]
})
```
1 change: 1 addition & 0 deletions src/actions/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ test('exports actions', () => {
"getFilterChanges": [Function],
"getFilterLogs": [Function],
"getGasPrice": [Function],
"getLogs": [Function],
"getPermissions": [Function],
"getTransaction": [Function],
"getTransactionConfirmations": [Function],
Expand Down
3 changes: 3 additions & 0 deletions src/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export {
getFilterChanges,
getFilterLogs,
getGasPrice,
getLogs,
getTransaction,
getTransactionConfirmations,
getTransactionCount,
Expand Down Expand Up @@ -52,6 +53,8 @@ export type {
GetFilterLogsArgs,
GetFilterLogsResponse,
GetGasPriceResponse,
GetLogsArgs,
GetLogsResponse,
GetTransactionArgs,
GetTransactionConfirmationsArgs,
GetTransactionConfirmationsResponse,
Expand Down
6 changes: 2 additions & 4 deletions src/actions/public/createEventFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type {
BlockTag,
ExtractArgsFromEventDefinition,
Filter,
Hex,
LogTopic,
} from '../../types'
import { getEventSignature, numberToHex } from '../../utils'

Expand All @@ -31,8 +31,6 @@ export type CreateEventFilterArgs<
)
export type CreateEventFilterResponse = Filter<'event'>

type EncodedArg = Hex | Hex[] | null

export async function createEventFilter<
TEventDefinition extends `${string}(${string})`,
>(
Expand All @@ -45,7 +43,7 @@ export async function createEventFilter<
toBlock,
}: CreateEventFilterArgs<TEventDefinition> = {},
): Promise<CreateEventFilterResponse> {
let topics: EncodedArg[] = []
let topics: LogTopic[] = []
if (event) {
topics = buildFilterTopics({ event, args })
}
Expand Down
105 changes: 105 additions & 0 deletions src/actions/public/getLogs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { afterAll, assertType, beforeAll, describe, expect, test } from 'vitest'

import {
accounts,
initialBlockNumber,
publicClient,
testClient,
transfer1Data,
usdcAddress,
vitalikAddress,
walletClient,
} from '../../_test'

import {
impersonateAccount,
mine,
setIntervalMining,
stopImpersonatingAccount,
} from '../test'
import { sendTransaction } from '../wallet'
import type { Log } from '../../types'
import { getLogs } from './getLogs'
import { getBlock } from './getBlock'

beforeAll(async () => {
await setIntervalMining(testClient, { interval: 0 })
await impersonateAccount(testClient, {
address: vitalikAddress,
})
})

afterAll(async () => {
await setIntervalMining(testClient, { interval: 1 })
await stopImpersonatingAccount(testClient, {
address: vitalikAddress,
})
})

test('default', async () => {
const logs = await getLogs(publicClient)
expect(logs).toMatchInlineSnapshot('[]')
})

describe('events', () => {
test('no args', async () => {
await sendTransaction(walletClient, {
from: vitalikAddress,
to: usdcAddress,
data: transfer1Data(accounts[0].address),
})
await sendTransaction(walletClient, {
from: vitalikAddress,
to: usdcAddress,
data: transfer1Data(accounts[1].address),
})
await mine(testClient, { blocks: 1 })

let logs = await getLogs(publicClient)
assertType<Log[]>(logs)
expect(logs.length).toBe(2)
})

test('args: event', async () => {
await sendTransaction(walletClient, {
from: vitalikAddress,
to: usdcAddress,
data: transfer1Data(accounts[0].address),
})
await sendTransaction(walletClient, {
from: vitalikAddress,
to: usdcAddress,
data: transfer1Data(accounts[1].address),
})

await mine(testClient, { blocks: 1 })

let logs = await getLogs(publicClient, {
event: 'Transfer(address from, address to, uint256 value)',
})
assertType<Log[]>(logs)
expect(logs.length).toBe(2)
})

test('args: fromBlock/toBlock', async () => {
let logs = await getLogs(publicClient, {
event: 'Transfer(address from, address to, uint256 value)',
fromBlock: initialBlockNumber - 5n,
toBlock: initialBlockNumber,
})
assertType<Log[]>(logs)
expect(logs.length).toBe(1056)
})

test('args: blockHash', async () => {
const block = await getBlock(publicClient, {
blockNumber: initialBlockNumber - 1n,
})
let logs = await getLogs(publicClient, {
event: 'Transfer(address from, address to, uint256 value)',
blockHash: block.hash!,
})
assertType<Log[]>(logs)
expect(logs.length).toBe(118)
})
})
Loading

3 comments on commit f908190

@vercel
Copy link

@vercel vercel bot commented on f908190 Feb 3, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

viem-playground – ./playgrounds/dev

viem-playground-wagmi-dev.vercel.app
viem-playground-git-main-wagmi-dev.vercel.app
viem-playground.vercel.app

@vercel
Copy link

@vercel vercel bot commented on f908190 Feb 3, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

viem-site – ./site

viem-site-wagmi-dev.vercel.app
viem-site.vercel.app
viem-site-git-main-wagmi-dev.vercel.app

@vercel
Copy link

@vercel vercel bot commented on f908190 Feb 3, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

viem-benchmark – ./playgrounds/benchmark

Please sign in to comment.