Skip to content

Commit

Permalink
feat: add args as property on parseEventLogs (#2533)
Browse files Browse the repository at this point in the history
* fix: resolves #2522

* chore: changeset

* chore: changeset

* docs: up

* chore: tweaks

* chore: format

---------

Co-authored-by: jxom <jxom@users.noreply.github.com>
  • Loading branch information
jxom and jxom authored Jul 25, 2024
1 parent 603227e commit a5d4ec4
Show file tree
Hide file tree
Showing 8 changed files with 1,138 additions and 25 deletions.
5 changes: 5 additions & 0 deletions .changeset/hip-clocks-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"viem": patch
---

Fixed support for filtering contract logs via its events on `abi` + `args`.
5 changes: 5 additions & 0 deletions .changeset/proud-eyes-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"viem": patch
---

Added `args` as a property to `parseEventLogs` to filter event logs by its arguments.
24 changes: 16 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

199 changes: 199 additions & 0 deletions site/pages/docs/contract/parseEventLogs.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,179 @@ export const client = createPublicClient({

:::

### Scoping to Arguments

You can scope the logs to arguments by providing the `args` argument:

:::code-group

```ts [example.ts]
import { parseEventLogs } from 'viem'
import { erc20Abi } from './abi'
import { client } from './client'

const receipt = await getTransactionReceipt(client, {
hash: '0xec23b2ba4bc59ba61554507c1b1bc91649e6586eb2dd00c728e8ed0db8bb37ea',
})

const logs = parseEventLogs({
abi: erc20Abi,
args: { // [!code focus]
from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // [!code focus]
}, // [!code focus]
logs: receipt.logs,
})
// [
// { args: { from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', ... }, eventName: '...', logIndex: 3, ... },
// { args: { from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', ... }, eventName: '...', logIndex: 7, ... },
// ...
// ]
```

```ts [abi.ts]
export const erc20Abi = [
...
{
inputs: [
{
indexed: true,
name: 'from',
type: 'address',
},
{ indexed: true, name: 'to', type: 'address' },
{
indexed: false,
name: 'value',
type: 'uint256',
},
],
name: 'Transfer',
type: 'event',
},
{
inputs: [
{
indexed: true,
name: 'owner',
type: 'address',
},
{
indexed: true,
name: 'spender',
type: 'address',
},
{
indexed: false,
name: 'value',
type: 'uint256',
},
],
name: 'Approval',
type: 'event',
}
...
] as const;
```

```ts [client.ts]
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'

export const client = createPublicClient({
chain: mainnet,
transport: http()
})
```

:::

You can also pass an array to scope multiple values of an argument:

:::code-group

```ts [example.ts]
import { parseEventLogs } from 'viem'
import { erc20Abi } from './abi'
import { client } from './client'

const receipt = await getTransactionReceipt(client, {
hash: '0xec23b2ba4bc59ba61554507c1b1bc91649e6586eb2dd00c728e8ed0db8bb37ea',
})

const logs = parseEventLogs({
abi: erc20Abi,
args: { // [!code focus]
from: [ // [!code focus]
'0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // [!code focus]
'0xd8da6bf26964af9d7eed9e03e53415d37aa96045', // [!code focus]
], // [!code focus]
}, // [!code focus]
logs: receipt.logs,
})
// [
// { args: { from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', ... }, eventName: '...', logIndex: 3, ... },
// { args: { from: '0xd8da6bf26964af9d7eed9e03e53415d37aa96045', ... }, eventName: '...', logIndex: 7, ... },
// ...
// ]
```

```ts [abi.ts]
export const erc20Abi = [
...
{
inputs: [
{
indexed: true,
name: 'from',
type: 'address',
},
{ indexed: true, name: 'to', type: 'address' },
{
indexed: false,
name: 'value',
type: 'uint256',
},
],
name: 'Transfer',
type: 'event',
},
{
inputs: [
{
indexed: true,
name: 'owner',
type: 'address',
},
{
indexed: true,
name: 'spender',
type: 'address',
},
{
indexed: false,
name: 'value',
type: 'uint256',
},
],
name: 'Approval',
type: 'event',
}
...
] as const;
```

```ts [client.ts]
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'

export const client = createPublicClient({
chain: mainnet,
transport: http()
})
```

:::

### Partial Decode

By default, if the `topics` and `data` does not conform to the ABI (a mismatch between the number of indexed/non-indexed arguments), `parseEventLogs` will not return return the decoded log.
Expand Down Expand Up @@ -366,6 +539,32 @@ const topics = parseEventLogs({
})
```

### args (optional)

- **Type:** `{ [property: string]: string | string[] | null }`

Arguments to scope the logs to.

```ts
const topics = parseEventLogs({
abi: wagmiAbi,
args: { // [!code focus]
from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // [!code focus]
}, // [!code focus]
logs: [{
blockNumber: 69420n,
data: '0x0000000000000000000000000000000000000000000000000000000000000001',
logIndex: 1,
topics: [
'0x406dade31f7ae4b5dbc276258c28dde5ae6d5c2773c5745802c493a2360e55e0',
'0x00000000000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266',
'0x0000000000000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8'
]
// ...
}]
})
```

### eventName (optional)

- **Type:** `string`
Expand Down
43 changes: 43 additions & 0 deletions src/actions/public/getContractEvents.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,49 @@ test('args: abi', async () => {
expect(logs.length).toBe(3)
})

test('args: args', async () => {
await writeContract(client, {
...usdcContractConfig,
functionName: 'transfer',
args: [accounts[0].address, 1n],
account: address.usdcHolder,
})
await writeContract(client, {
...usdcContractConfig,
functionName: 'transfer',
args: [accounts[1].address, 1n],
account: address.usdcHolder,
})
await writeContract(client, {
...daiContractConfig,
functionName: 'transfer',
args: [accounts[1].address, 1n],
account: address.daiHolder,
})
await writeContract(client, {
...usdcContractConfig,
functionName: 'approve',
args: [accounts[1].address, 1n],
account: address.usdcHolder,
})
await mine(client, { blocks: 1 })

const logs = await getContractEvents(client, {
abi: erc20Abi,
args: {
from: address.daiHolder,
to: accounts[1].address,
},
})
expect(logs.length).toBe(1)
expect(logs[0].eventName).toEqual('Transfer')
expect(logs[0].args).toEqual({
from: getAddress(address.daiHolder),
to: getAddress(accounts[1].address),
value: 1n,
})
})

test('args: eventName', async () => {
await writeContract(client, {
...usdcContractConfig,
Expand Down
3 changes: 2 additions & 1 deletion src/actions/public/getLogs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ export async function getLogs<
encodeEventTopics({
abi: [event],
eventName: (event as AbiEvent).name,
args,
args: events_ ? undefined : args,
} as EncodeEventTopicsParameters),
)
// TODO: Clean up type casting
Expand Down Expand Up @@ -205,6 +205,7 @@ export async function getLogs<
>
return parseEventLogs({
abi: events,
args: args as any,
logs: formattedLogs,
strict,
}) as unknown as GetLogsReturnType<
Expand Down
Loading

0 comments on commit a5d4ec4

Please sign in to comment.