-
Notifications
You must be signed in to change notification settings - Fork 104
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
feat: filter types #1198
Comments
The I'm still not sure about Another idea (I know it's controversial 😉 ) would be to add a field selection option here, eg: type LogFilter = {
type: "log";
chainId: number;
address: Address | Address[] | Factory | undefined;
topics: LogTopic[];
fromBlock: number;
toBlock: number | undefined;
// includeTransactionReceipts: boolean;
include: LogFilterField[] | undefined;
};
type LogFilterField =
"log.address" |
"log.topics" |
"log.data" |
"block.number" |
"block.timestamp" |
"transaction.hash" |
"transaction.input" |
"transaction.gasUsed" // if included, the sync engine must fetch receipts
// ... For backwards compatibility (and perhaps as the forever default) the high-level |
I definitely like the idea of just joining new properties into |
I'm not quite convinced that traces and transactions should be combined into one filter type. From the EVM perspective, I can see why the EOA transaction (input + receipt) is the same logical "event" as the corresponding CALL trace. However, if we look at the fields available to us from the RPC responses, the EOA/"topCall" transaction has far more fields. For mainnet transaction Transaction input & receipt (from // input
{
blockHash:"0x1197de9312833dc933878c1ab0134b2e02bf0218f808dc3179c83f4d185267d2",
blockNumber:"0x1418caa",
hash:"0x50560570c4984213a573a28196f84669aaf179b0f1253b908b7621070b45f512",
yParity:"0x1",
accessList:[],
transactionIndex:"0x0",
type:"0x2",
nonce:"0x6",
input:"0x0162e2d...",
r:"0x4ac0fc4c3de38288d3b155905ed3ed9066e33381e6ddcac3094642f0eb47c42f",
s:"0x32002d52e8fbfc6ae6a196c73595669ce8a663c4bb737f6dc466a6f82c12aefc",
chainId:"0x1",
v:"0x1",
gas:"0x741a2",
maxPriorityFeePerGas:"0x9b10b18400",
from:"0xc6a7a6fc4a2ac1b8fc3ec1c0e343e2855aa35afc",
to:"0x3328f7f4a1d1c57c35df56bbf0c9dcafca309c49",
maxFeePerGas:"0x9f8956054d",
value:"0x1bc16d674ec80000",
gasPrice:"0x9e5f50134d"
}
// receipt
{
transactionHash:"0x50560570c4984213a573a28196f84669aaf179b0f1253b908b7621070b45f512",
blockHash:"0x1197de9312833dc933878c1ab0134b2e02bf0218f808dc3179c83f4d185267d2",
blockNumber:"0x1418caa",
logsBloom:"0x0024...",
gasUsed:"0x2954f",
contractAddress:null,
cumulativeGasUsed:"0x2954f",
transactionIndex:"0x0",
from:"0xc6a7a6fc4a2ac1b8fc3ec1c0e343e2855aa35afc",
to:"0x3328f7f4a1d1c57c35df56bbf0c9dcafca309c49",
type:"0x2",
effectiveGasPrice:"0x9e5f50134d",
logs: [ ... ],
status:"0x1"
} Corresponding CALL trace (from {
from:"0xc6a7a6fc4a2ac1b8fc3ec1c0e343e2855aa35afc",
gas:"0x741a2",
gasUsed:"0x2954f",
to:"0x3328f7f4a1d1c57c35df56bbf0c9dcafca309c49",
input:"0x0162e2d...",
calls: [ ... ],
value:"0x1bc16d674ec80000",
type:"CALL"
} The specific fields that seem useful for indexing in the EOA input/receipt are With that said, it may also be useful to specify the return type for each filter and the available "joined" objects (which would form the arguments to the
|
Updated filter types with corresponding raw events and field selection type LogFilter = {
type: "log";
chainId: number;
address: Address | Address[] | Factory | undefined;
topics: LogTopic[];
fromBlock: number;
toBlock: number | undefined;
include: (
| "block.baseFeePerGas"
| "block.difficulty"
| "block.extraData"
| "block.gasLimit"
| "block.gasUsed"
| "block.hash"
| "block.logsBloom"
| "block.miner"
| "block.mixHash"
| "block.nonce"
| "block.number"
| "block.parentHash"
| "block.receiptsRoot"
| "block.sha3Uncles"
| "block.size"
| "block.stateRoot"
| "block.timestamp"
| "block.totalDifficulty"
| "block.transactionsRoot"
| "transaction.blockHash"
| "transaction.blockNumber"
| "transaction.from"
| "transaction.gas"
| "transaction.hash"
| "transaction.input"
| "transaction.nonce"
| "transaction.r"
| "transaction.s"
| "transaction.to"
| "transaction.transactionIndex"
| "transaction.v"
| "transaction.value"
| "log.address"
| "log.blockHash"
| "log.blockNumber"
| "log.data"
| "log.logIndex"
| "log.removed"
| "log.topics"
| "log.transactionHash"
| "log.transactionIndex"
| "transactionReceipt.contractAddress"
| "transactionReceipt.cumulativeGasUsed"
| "transactionReceipt.effectiveGasPrice"
| "transactionReceipt.gasUsed"
| "transactionReceipt.logs"
)[] | undefined;
};
type LogFilterRawEvent = {
chainId: number;
sourceIndex: number;
checkpoint: string;
log: Log;
block: Block;
transaction: Transaction;
transactionReceipt: TransactionReceipt;
};
type BlockFilter = {
type: "block";
chainId: number;
interval: number;
offset: number;
fromBlock: number;
toBlock: number | undefined;
include: (
| "block.baseFeePerGas"
| "block.difficulty"
| "block.extraData"
| "block.gasLimit"
| "block.gasUsed"
| "block.hash"
| "block.logsBloom"
| "block.miner"
| "block.mixHash"
| "block.nonce"
| "block.number"
| "block.parentHash"
| "block.receiptsRoot"
| "block.sha3Uncles"
| "block.size"
| "block.stateRoot"
| "block.timestamp"
| "block.totalDifficulty"
| "block.transactionsRoot"
)[] | undefined;
};
type BlockFilterRawEvent = {
chainId: number;
sourceIndex: number;
checkpoint: string;
block: Block;
};
type TransferFilter = {
type: "transfer";
chainId: number;
fromAddress: Address | Address[] | Factory | undefined;
toAddress: Address | Address[] | Factory | undefined;
fromBlock: number;
toBlock: number | undefined;
include: (
| "block.baseFeePerGas"
| "block.difficulty"
| "block.extraData"
| "block.gasLimit"
| "block.gasUsed"
| "block.hash"
| "block.logsBloom"
| "block.miner"
| "block.mixHash"
| "block.nonce"
| "block.number"
| "block.parentHash"
| "block.receiptsRoot"
| "block.sha3Uncles"
| "block.size"
| "block.stateRoot"
| "block.timestamp"
| "block.totalDifficulty"
| "block.transactionsRoot"
| "transaction.blockHash"
| "transaction.blockNumber"
| "transaction.from"
| "transaction.gas"
| "transaction.hash"
| "transaction.input"
| "transaction.nonce"
| "transaction.r"
| "transaction.s"
| "transaction.to"
| "transaction.transactionIndex"
| "transaction.v"
| "transaction.value"
| "transactionReceipt.contractAddress"
| "transactionReceipt.cumulativeGasUsed"
| "transactionReceipt.effectiveGasPrice"
| "transactionReceipt.gasUsed"
| "transactionReceipt.logs"
| "trace.gas"
| "trace.gasUsed"
| "trace.from"
| "trace.to"
| "trace.input"
| "trace.output"
| "trace.value"
)[] | undefined;
};
type TransferFilterRawEvent = {
chainId: number;
sourceIndex: number;
checkpoint: string;
block: Block;
transaction: Transaction;
transactionReceipt: TransactionReceipt;
trace: Trace;
};
type TransactionFilter = {
type: "transaction";
chainId: number;
fromAddress: Address | Address[] | Factory | undefined;
toAddress: Address | Address[] | Factory | undefined;
functionSelectors: Hex | Hex[] | undefined;
includeInner: boolean;
includeFailed: boolean;
callType: "call" | "staticcall" | "delegatecall" | "selfdestruct" | "create" | "create2" | "callcode" | undefined;
fromBlock: number;
toBlock: number | undefined;
include: (
| "block.baseFeePerGas"
| "block.difficulty"
| "block.extraData"
| "block.gasLimit"
| "block.gasUsed"
| "block.hash"
| "block.logsBloom"
| "block.miner"
| "block.mixHash"
| "block.nonce"
| "block.number"
| "block.parentHash"
| "block.receiptsRoot"
| "block.sha3Uncles"
| "block.size"
| "block.stateRoot"
| "block.timestamp"
| "block.totalDifficulty"
| "block.transactionsRoot"
| "transaction.blockHash"
| "transaction.blockNumber"
| "transaction.from"
| "transaction.gas"
| "transaction.hash"
| "transaction.input"
| "transaction.nonce"
| "transaction.r"
| "transaction.s"
| "transaction.to"
| "transaction.transactionIndex"
| "transaction.v"
| "transaction.value"
| "transactionReceipt.contractAddress"
| "transactionReceipt.cumulativeGasUsed"
| "transactionReceipt.effectiveGasPrice"
| "transactionReceipt.gasUsed"
| "transactionReceipt.logs"
| "trace.gas"
| "trace.gasUsed"
| "trace.from"
| "trace.to"
| "trace.input"
| "trace.output"
| "trace.value"
)[] | undefined;
};
type TransactionFilterRawEvent = {
chainId: number;
sourceIndex: number;
checkpoint: string;
block: Block;
transaction: Transaction;
transactionReceipt: TransactionReceipt;
trace: Trace;
}; NotesI can see what you are getting at with some of the return data being confusing, ( |
Edit: Decided against this (for now). I think we should break out log topics and support passing a factory for those fields. This would unblock use cases like "filter for any ERC20 transfer sent to any smart account created by a specific factory". type LogFilter = {
type: "log";
chainId: number;
address: Address | Address[] | Factory | undefined;
topic0: LogTopic | LogTopic[] | Factory | undefined;
topic1: LogTopic | LogTopic[] | Factory | undefined;
topic2: LogTopic | LogTopic[] | Factory | undefined;
topic3: LogTopic | LogTopic[] | Factory | undefined;
fromBlock: number;
toBlock: number | undefined;
include: ...
} Note that there may be some complexity in the implementation, because addresses are 20 bytes but topics are 32. |
Latest iteration. Pretty happy with it! Notes:
type LogFilter = {
chainId: number;
fromBlock: number | undefined;
toBlock: number | undefined;
address: Address | Address[] | Factory | undefined;
topic0: LogTopic | undefined;
topic1: LogTopic | undefined;
topic2: LogTopic | undefined;
topic3: LogTopic | undefined;
// log + block + transaction fields
include: string[] | undefined;
};
type BlockFilter = {
chainId: number;
fromBlock: number | undefined;
toBlock: number | undefined;
interval: number;
offset: number;
// block fields
include: string[] | undefined;
};
type TransactionFilter = {
chainId: number;
fromBlock: number | undefined;
toBlock: number | undefined;
fromAddress: Address | Address[] | Factory | undefined;
toAddress: Address | Address[] | Factory | undefined;
includeReverted: boolean;
// transaction + block + trace fields
include: string[] | undefined;
};
type TraceFilter = {
chainId: number;
fromBlock: number | undefined;
toBlock: number | undefined;
fromAddress: Address | Address[] | Factory | undefined;
toAddress: Address | Address[] | Factory | undefined;
functionSelector: Hex | Hex[] | undefined;
callType: "call" | "staticcall" | "delegatecall" | "selfdestruct" | "create" | "create2" | "callcode" | undefined;
includeReverted: boolean;
// trace + block + transaction fields
include: string[] | undefined;
};
type TransferFilter = {
chainId: number;
fromBlock: number | undefined;
toBlock: number | undefined;
fromAddress: Address | Address[] | Factory | undefined;
toAddress: Address | Address[] | Factory | undefined;
// transfer + block + transaction + trace fields
include: string[] | undefined;
}; |
Tracking issue for all filter related features.
Filters should be deliberately designed, expressive, and modeled after the fundamentals of the Ethereum blockchain, not necessarily the Ethereum standard JSON-RPC. Raw blockchain data that is matched by a filter becomes an event, the arguments to an indexing function.
Proposed filter types
Few things I'm not sure about:
includeTransactionReceipt
necessary? Does in need to include thelogs
property?ponder.config.ts
The config should provide high level abstractions, such as
contracts
andaccounts
. These high-levelsources
also have the ability to post-process a filter result and add decoded args, etc.factory()
I think it could be nice to have a helper function that can be used in any
address
field.The text was updated successfully, but these errors were encountered: