Skip to content

Commit

Permalink
fix: skip invalid logs (#326)
Browse files Browse the repository at this point in the history
* fix: skip invalid logs

* chore: format

* chore: changeset
  • Loading branch information
jxom authored Apr 7, 2023
1 parent cfa1224 commit c83616a
Show file tree
Hide file tree
Showing 24 changed files with 902 additions and 245 deletions.
5 changes: 5 additions & 0 deletions .changeset/spotty-chefs-glow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"viem": patch
---

Fixed an issue where filtered logs that do not conform to the provided ABI would cause `getLogs`, `getFilterLogs` or `getFilterChanges` to throw – these logs are now skipped. See [#323](https://github.com/wagmi-dev/viem/issues/323#issuecomment-1499654052) for more info.
12 changes: 12 additions & 0 deletions contracts/src/ERC20InvalidTransferEvent.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.13;

contract ERC20InvalidTransferEvent {
// Non-conforming `to` parameter (not indexed).
event Transfer(address indexed from, address to, uint256 value);

function transfer(address recipient, uint256 amount) public {
emit Transfer(msg.sender, recipient, amount);
return;
}
}
1 change: 1 addition & 0 deletions src/_test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export {
deploy,
deployBAYC,
deployEnsAvatarTokenUri,
deployErc20InvalidTransferEvent,
publicClient,
testClient,
walletClient,
Expand Down
15 changes: 14 additions & 1 deletion src/_test/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* c8 ignore start */
import type { Abi } from 'abitype'
import ensAvatarTokenUri from '../../contracts/out/EnsAvatarTokenUri.sol/EnsAvatarTokenUri.json'
import erc20InvalidTransferEvent from '../../contracts/out/ERC20InvalidTransferEvent.sol/ERC20InvalidTransferEvent.json'
import errorsExample from '../../contracts/out/ErrorsExample.sol/ErrorsExample.json'
import {
deployContract,
Expand All @@ -26,7 +27,11 @@ import { RpcError } from '../types/eip1193'
import { rpc } from '../utils'
import { baycContractConfig, ensRegistryConfig } from './abis'
import { accounts, address, localWsUrl } from './constants'
import { ensAvatarTokenUriABI, errorsExampleABI } from './generated'
import {
ensAvatarTokenUriABI,
erc20InvalidTransferEventABI,
errorsExampleABI,
} from './generated'

import type { RequestListener } from 'http'
import { createServer } from 'http'
Expand Down Expand Up @@ -190,6 +195,14 @@ export async function deployEnsAvatarTokenUri() {
})
}

export async function deployErc20InvalidTransferEvent() {
return deploy({
abi: erc20InvalidTransferEventABI,
bytecode: erc20InvalidTransferEvent.bytecode.object as Hex,
account: accounts[0].address,
})
}

export async function setBlockNumber(blockNumber: bigint) {
await reset(testClient, {
blockNumber,
Expand Down
93 changes: 93 additions & 0 deletions src/actions/public/getFilterChanges.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import { afterAll, assertType, beforeAll, describe, expect, test } from 'vitest'
import {
accounts,
address,
deployErc20InvalidTransferEvent,
initialBlockNumber,
publicClient,
testClient,
walletClient,
usdcContractConfig,
} from '../../_test'
import { erc20InvalidTransferEventABI } from '../../_test/generated'

import {
impersonateAccount,
Expand Down Expand Up @@ -47,6 +49,27 @@ const event = {
name: 'Transfer',
type: 'event',
},
invalid: {
inputs: [
{
indexed: true,
name: 'from',
type: 'address',
},
{
indexed: false,
name: 'to',
type: 'address',
},
{
indexed: false,
name: 'value',
type: 'uint256',
},
],
name: 'Transfer',
type: 'event',
},
unnamed: {
inputs: [
{
Expand Down Expand Up @@ -870,3 +893,73 @@ describe('events', () => {
])
})
})

describe('skip invalid logs', () => {
test('indexed params mismatch', async () => {
const { contractAddress } = await deployErc20InvalidTransferEvent()

const filter = await createEventFilter(publicClient, {
event: event.default,
})

await writeContract(walletClient, {
...usdcContractConfig,
functionName: 'transfer',
args: [accounts[0].address, 1n],
account: address.vitalik,
})
await writeContract(walletClient, {
abi: erc20InvalidTransferEventABI,
address: contractAddress!,
functionName: 'transfer',
args: [accounts[0].address, 1n],
account: address.vitalik,
})
await writeContract(walletClient, {
abi: erc20InvalidTransferEventABI,
address: contractAddress!,
functionName: 'transfer',
args: [accounts[1].address, 1n],
account: address.vitalik,
})
await mine(testClient, { blocks: 1 })

const logs = await getFilterChanges(publicClient, { filter })
assertType<Log[]>(logs)
expect(logs.length).toBe(1)
})

test('non-indexed params mismatch', async () => {
const { contractAddress } = await deployErc20InvalidTransferEvent()

const filter = await createEventFilter(publicClient, {
event: event.invalid,
})

await writeContract(walletClient, {
...usdcContractConfig,
functionName: 'transfer',
args: [accounts[0].address, 1n],
account: address.vitalik,
})
await writeContract(walletClient, {
abi: erc20InvalidTransferEventABI,
address: contractAddress!,
functionName: 'transfer',
args: [accounts[0].address, 1n],
account: address.vitalik,
})
await writeContract(walletClient, {
abi: erc20InvalidTransferEventABI,
address: contractAddress!,
functionName: 'transfer',
args: [accounts[1].address, 1n],
account: address.vitalik,
})
await mine(testClient, { blocks: 1 })

const logs = await getFilterChanges(publicClient, { filter })
assertType<Log[]>(logs)
expect(logs.length).toBe(2)
})
})
36 changes: 24 additions & 12 deletions src/actions/public/getFilterChanges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,28 @@ export async function getFilterChanges<
method: 'eth_getFilterChanges',
params: [filter.id],
})
return logs.map((log) => {
if (typeof log === 'string') return log
const { eventName, args } =
'abi' in filter && filter.abi
? decodeEventLog({
abi: filter.abi,
data: log.data,
topics: log.topics as any,
})
: { eventName: undefined, args: undefined }
return formatLog(log, { args, eventName })
}) as GetFilterChangesReturnType<TFilterType, TAbiEvent, TAbi, TEventName>
return logs
.map((log) => {
if (typeof log === 'string') return log
try {
const { eventName, args } =
'abi' in filter && filter.abi
? decodeEventLog({
abi: filter.abi,
data: log.data,
topics: log.topics as any,
})
: { eventName: undefined, args: undefined }
return formatLog(log, { args, eventName })
} catch {
// Skip log if there is an error decoding (e.g. indexed/non-indexed params mismatch).
return
}
})
.filter(Boolean) as GetFilterChangesReturnType<
TFilterType,
TAbiEvent,
TAbi,
TEventName
>
}
93 changes: 93 additions & 0 deletions src/actions/public/getFilterLogs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import { afterAll, assertType, beforeAll, describe, expect, test } from 'vitest'
import {
accounts,
address,
deployErc20InvalidTransferEvent,
initialBlockNumber,
publicClient,
testClient,
usdcContractConfig,
walletClient,
} from '../../_test'
import { erc20InvalidTransferEventABI } from '../../_test/generated'

import {
impersonateAccount,
Expand Down Expand Up @@ -45,6 +47,27 @@ const event = {
name: 'Transfer',
type: 'event',
},
invalid: {
inputs: [
{
indexed: true,
name: 'from',
type: 'address',
},
{
indexed: false,
name: 'to',
type: 'address',
},
{
indexed: false,
name: 'value',
type: 'uint256',
},
],
name: 'Transfer',
type: 'event',
},
unnamed: {
inputs: [
{
Expand Down Expand Up @@ -746,3 +769,73 @@ describe('raw events', () => {
])
})
})

describe('skip invalid logs', () => {
test('indexed params mismatch', async () => {
const { contractAddress } = await deployErc20InvalidTransferEvent()

const filter = await createEventFilter(publicClient, {
event: event.default,
})

await writeContract(walletClient, {
...usdcContractConfig,
functionName: 'transfer',
args: [accounts[0].address, 1n],
account: address.vitalik,
})
await writeContract(walletClient, {
abi: erc20InvalidTransferEventABI,
address: contractAddress!,
functionName: 'transfer',
args: [accounts[0].address, 1n],
account: address.vitalik,
})
await writeContract(walletClient, {
abi: erc20InvalidTransferEventABI,
address: contractAddress!,
functionName: 'transfer',
args: [accounts[1].address, 1n],
account: address.vitalik,
})
await mine(testClient, { blocks: 1 })

const logs = await getFilterLogs(publicClient, { filter })
assertType<Log[]>(logs)
expect(logs.length).toBe(1)
})

test('non-indexed params mismatch', async () => {
const { contractAddress } = await deployErc20InvalidTransferEvent()

const filter = await createEventFilter(publicClient, {
event: event.invalid,
})

await writeContract(walletClient, {
...usdcContractConfig,
functionName: 'transfer',
args: [accounts[0].address, 1n],
account: address.vitalik,
})
await writeContract(walletClient, {
abi: erc20InvalidTransferEventABI,
address: contractAddress!,
functionName: 'transfer',
args: [accounts[0].address, 1n],
account: address.vitalik,
})
await writeContract(walletClient, {
abi: erc20InvalidTransferEventABI,
address: contractAddress!,
functionName: 'transfer',
args: [accounts[1].address, 1n],
account: address.vitalik,
})
await mine(testClient, { blocks: 1 })

const logs = await getFilterLogs(publicClient, { filter })
assertType<Log[]>(logs)
expect(logs.length).toBe(2)
})
})
33 changes: 22 additions & 11 deletions src/actions/public/getFilterLogs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,26 @@ export async function getFilterLogs<
method: 'eth_getFilterLogs',
params: [filter.id],
})
return logs.map((log) => {
const { eventName, args } =
'abi' in filter && filter.abi
? decodeEventLog({
abi: filter.abi,
data: log.data,
topics: log.topics as any,
})
: { eventName: undefined, args: undefined }
return formatLog(log, { args, eventName })
}) as unknown as GetFilterLogsReturnType<TAbiEvent, TAbi, TEventName>
return logs
.map((log) => {
try {
const { eventName, args } =
'abi' in filter && filter.abi
? decodeEventLog({
abi: filter.abi,
data: log.data,
topics: log.topics as any,
})
: { eventName: undefined, args: undefined }
return formatLog(log, { args, eventName })
} catch {
// Skip log if there is an error decoding (e.g. indexed/non-indexed params mismatch).
return
}
})
.filter(Boolean) as unknown as GetFilterLogsReturnType<
TAbiEvent,
TAbi,
TEventName
>
}
Loading

2 comments on commit c83616a

@vercel
Copy link

@vercel vercel bot commented on c83616a Apr 7, 2023

Choose a reason for hiding this comment

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

@vercel
Copy link

@vercel vercel bot commented on c83616a Apr 7, 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/browser

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

Please sign in to comment.