Skip to content

Commit 22b7991

Browse files
authored
Enable transactions look-up by signatures (#117)
### TL;DR Implemented signature lookup for transactions and improved query filtering. ### What changed? - Activated signature lookup in `GetTransactionsByContractAndSignature` function - Exported `ExtractFunctionSelector` function - Updated ClickHouse queries to include function selector for transactions - Generalised signature clause handling for both logs and transactions in ClickHouse queries ### How to test? 1. Make a GET request to `/{chainId}/transactions/{to}/{signature}` endpoint with a valid contract address and function signature ``` curl --location 'http://localhost:3000/660279/transactions/0x6e55472109e6abe4054a8e8b8d9edffcb31032c5/setMinDstGas(uint16,uint16,uint256)?limit=1' \ --header 'Authorization: Basic ...' ``` 2. Verify that the returned transactions are filtered correctly based on the provided signature ```json { "meta": { "chain_id": 660279, "address": "0x6e55472109e6abe4054a8e8b8d9edffcb31032c5", "signature": "0xdf2a5b3b87f900bae4c01587a04a2d9f5990f0066e443e87fe88cf2ce9974ff0", "page": 0, "limit": 5, "total_items": 5, "total_pages": 0 }, "data": [ { ... "to_address": "0x6e55472109e6abe4054a8e8b8d9edffcb31032c5", "value": 0, "gas": 52119, "gas_price": 1600000000, "data": "0xdf2a5b3b00000000000000000000000000000000000000000000000000000000000000af00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000030d40", "function_selector": "0xdf2a5b3b", ... } ] } ``` ### Why make this change? This change enables more precise filtering of transactions based on function signatures, improving the ability to query specific contract interactions. It also aligns the transaction querying capabilities with those already available for logs, providing a more consistent and powerful API for data retrieval.
2 parents f5ae429 + 2bdd298 commit 22b7991

File tree

3 files changed

+22
-11
lines changed

3 files changed

+22
-11
lines changed

internal/handlers/transactions_handlers.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package handlers
33
import (
44
"net/http"
55

6+
"github.com/ethereum/go-ethereum/crypto"
67
"github.com/gin-gonic/gin"
78
"github.com/rs/zerolog/log"
89
"github.com/thirdweb-dev/indexer/api"
10+
"github.com/thirdweb-dev/indexer/internal/rpc"
911
"github.com/thirdweb-dev/indexer/internal/storage"
1012
)
1113

@@ -104,8 +106,8 @@ func GetTransactionsByContract(c *gin.Context) {
104106
// @Router /{chainId}/transactions/{to}/{signature} [get]
105107
func GetTransactionsByContractAndSignature(c *gin.Context) {
106108
to := c.Param("to")
107-
// TODO: Implement signature lookup before activating this
108-
handleTransactionsRequest(c, to, "")
109+
signature := c.Param("signature")
110+
handleTransactionsRequest(c, to, signature)
109111
}
110112

111113
func handleTransactionsRequest(c *gin.Context, contractAddress, signature string) {
@@ -122,10 +124,9 @@ func handleTransactionsRequest(c *gin.Context, contractAddress, signature string
122124
}
123125

124126
signatureHash := ""
125-
// TODO: implement signature lookup
126-
// if signature != "" {
127-
// signatureHash = crypto.Keccak256Hash([]byte(signature)).Hex()
128-
// }
127+
if signature != "" {
128+
signatureHash = rpc.ExtractFunctionSelector(crypto.Keccak256Hash([]byte(signature)).Hex())
129+
}
129130

130131
mainStorage, err := getMainStorage()
131132
if err != nil {

internal/rpc/serializer.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ func serializeTransaction(chainId *big.Int, tx map[string]interface{}, blockTime
185185
Gas: hexToUint64(tx["gas"]),
186186
GasPrice: hexToBigInt(tx["gasPrice"]),
187187
Data: interfaceToString(tx["input"]),
188-
FunctionSelector: extractFunctionSelector(interfaceToString(tx["input"])),
188+
FunctionSelector: ExtractFunctionSelector(interfaceToString(tx["input"])),
189189
MaxFeePerGas: hexToBigInt(tx["maxFeePerGas"]),
190190
MaxPriorityFeePerGas: hexToBigInt(tx["maxPriorityFeePerGas"]),
191191
TransactionType: uint8(hexToUint64(tx["type"])),
@@ -270,7 +270,7 @@ func serializeTransaction(chainId *big.Int, tx map[string]interface{}, blockTime
270270
/**
271271
* Extracts the function selector (first 4 bytes) from a transaction input.
272272
*/
273-
func extractFunctionSelector(s string) string {
273+
func ExtractFunctionSelector(s string) string {
274274
if len(s) < 10 {
275275
return ""
276276
}

internal/storage/clickhouse.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ func (c *ClickHouseConnector) GetBlocks(qf QueryFilter) (blocks []common.Block,
339339
}
340340

341341
func (c *ClickHouseConnector) GetTransactions(qf QueryFilter) (QueryResult[common.Transaction], error) {
342-
columns := "chain_id, hash, nonce, block_hash, block_number, block_timestamp, transaction_index, from_address, to_address, value, gas, gas_price, data, max_fee_per_gas, max_priority_fee_per_gas, transaction_type, r, s, v, access_list"
342+
columns := "chain_id, hash, nonce, block_hash, block_number, block_timestamp, transaction_index, from_address, to_address, value, gas, gas_price, data, function_selector, max_fee_per_gas, max_priority_fee_per_gas, transaction_type, r, s, v, access_list"
343343
return executeQuery[common.Transaction](c, "transactions", columns, qf, scanTransaction)
344344
}
345345

@@ -359,7 +359,7 @@ func (c *ClickHouseConnector) GetAggregations(table string, qf QueryFilter) (Que
359359
}
360360
query = addContractAddress(table, query, qf.ContractAddress)
361361
if qf.Signature != "" {
362-
query += fmt.Sprintf(" AND topic_0 = '%s'", qf.Signature)
362+
query = addSignatureClause(table, query, qf.Signature)
363363
}
364364
for key, value := range qf.FilterParams {
365365
query = addFilterParams(key, strings.ToLower(value), query)
@@ -452,7 +452,7 @@ func (c *ClickHouseConnector) buildQuery(table, columns string, qf QueryFilter)
452452

453453
// Add signature clause
454454
if qf.Signature != "" {
455-
query += fmt.Sprintf(" AND topic_0 = '%s'", qf.Signature)
455+
query = addSignatureClause(table, query, qf.Signature)
456456
}
457457
// Add filter params
458458
for key, value := range qf.FilterParams {
@@ -516,6 +516,15 @@ func addContractAddress(table, query string, contractAddress string) string {
516516
return query
517517
}
518518

519+
func addSignatureClause(table, query, signature string) string {
520+
if table == "logs" {
521+
query += fmt.Sprintf(" AND topic_0 = '%s'", signature)
522+
} else if table == "transactions" {
523+
query += fmt.Sprintf(" AND function_selector = '%s'", signature)
524+
}
525+
return query
526+
}
527+
519528
func getTopicValueFormat(topic string) string {
520529
if topic == "" {
521530
// if there is no indexed topic, indexer stores an empty string
@@ -545,6 +554,7 @@ func scanTransaction(rows driver.Rows) (common.Transaction, error) {
545554
&tx.Gas,
546555
&tx.GasPrice,
547556
&tx.Data,
557+
&tx.FunctionSelector,
548558
&tx.MaxFeePerGas,
549559
&tx.MaxPriorityFeePerGas,
550560
&tx.TransactionType,

0 commit comments

Comments
 (0)