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

fix: skip invalid logs #326

Merged
merged 3 commits into from
Apr 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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