diff --git a/internal/common/utils.go b/internal/common/utils.go index 984a83b..a0ffc7c 100644 --- a/internal/common/utils.go +++ b/internal/common/utils.go @@ -1,6 +1,11 @@ package common -import "math/big" +import ( + "fmt" + "math/big" + "strings" + "unicode" +) func BigIntSliceToChunks(values []*big.Int, chunkSize int) [][]*big.Int { if chunkSize >= len(values) || chunkSize <= 0 { @@ -16,3 +21,151 @@ func BigIntSliceToChunks(values []*big.Int, chunkSize int) [][]*big.Int { } return chunks } + +// StripPayload removes parameter names, 'indexed' keywords, +// and extra whitespaces from a Solidity function or event signature. +func StripPayload(signature string) string { + // Find the index of the first '(' and last ')' + start := strings.Index(signature, "(") + end := strings.LastIndex(signature, ")") + if start == -1 || end == -1 || end <= start { + // Return the original signature if it doesn't match the expected pattern + return signature + } + + functionName := strings.TrimSpace(signature[:start]) + paramsStr := signature[start+1 : end] + + // Parse parameters + strippedParams := parseParameters(paramsStr) + + // Reconstruct the cleaned-up signature + strippedSignature := fmt.Sprintf("%s(%s)", functionName, strings.Join(strippedParams, ",")) + return strippedSignature +} + +// parseParameters parses the parameter string and returns a slice of cleaned-up parameter types +func parseParameters(paramsStr string) []string { + var params []string + var currentParam strings.Builder + bracketDepth := 0 + var inType bool // Indicates if we are currently parsing a type + + runes := []rune(paramsStr) + i := 0 + for i < len(runes) { + char := runes[i] + switch char { + case '(', '[', '{': + bracketDepth++ + inType = true + currentParam.WriteRune(char) + i++ + case ')', ']', '}': + bracketDepth-- + currentParam.WriteRune(char) + i++ + case ',': + if bracketDepth == 0 { + // End of current parameter + paramType := cleanType(currentParam.String()) + if paramType != "" { + params = append(params, paramType) + } + currentParam.Reset() + inType = false + i++ + } else { + currentParam.WriteRune(char) + i++ + } + case ' ': + if inType { + currentParam.WriteRune(char) + } + i++ + default: + // Check if the word is a keyword to ignore + if unicode.IsLetter(char) { + wordStart := i + for i < len(runes) && (unicode.IsLetter(runes[i]) || unicode.IsDigit(runes[i])) { + i++ + } + word := string(runes[wordStart:i]) + + // Ignore 'indexed' and parameter names + if isType(word) { + inType = true + currentParam.WriteString(word) + } else if word == "indexed" { + // Skip 'indexed' + inType = false + } else { + // Ignore parameter names + if inType { + // If we are in the middle of parsing a type and encounter a parameter name, skip it + inType = false + } + } + } else { + if inType { + currentParam.WriteRune(char) + } + i++ + } + } + } + + // Add the last parameter + if currentParam.Len() > 0 { + paramType := cleanType(currentParam.String()) + if paramType != "" { + params = append(params, paramType) + } + } + + return params +} + +// cleanType cleans up a parameter type string by removing extra spaces and 'tuple' keyword +func cleanType(param string) string { + // Remove 'tuple' keyword + param = strings.ReplaceAll(param, "tuple", "") + // Remove 'indexed' keyword + param = strings.ReplaceAll(param, "indexed", "") + // Remove any parameter names (already handled in parsing) + param = strings.TrimSpace(param) + // Remove extra whitespaces + param = strings.Join(strings.Fields(param), "") + return param +} + +// isType checks if a word is a Solidity type +func isType(word string) bool { + types := map[string]bool{ + "uint": true, + "int": true, + "uint8": true, + "int8": true, + "uint16": true, + "int16": true, + "uint32": true, + "int32": true, + "uint64": true, + "int64": true, + "uint128": true, + "int128": true, + "uint256": true, + "int256": true, + "address": true, + "bool": true, + "string": true, + "bytes": true, + "fixed": true, + "ufixed": true, + "function": true, + // Add other types as needed + } + + return types[word] +} diff --git a/internal/handlers/logs_handlers.go b/internal/handlers/logs_handlers.go index e4db770..0b22a11 100644 --- a/internal/handlers/logs_handlers.go +++ b/internal/handlers/logs_handlers.go @@ -10,6 +10,7 @@ import ( "github.com/thirdweb-dev/indexer/api" config "github.com/thirdweb-dev/indexer/configs" "github.com/thirdweb-dev/indexer/internal/storage" + "github.com/thirdweb-dev/indexer/internal/common" ) // package-level variables @@ -105,7 +106,8 @@ func GetLogsByContract(c *gin.Context) { func GetLogsByContractAndSignature(c *gin.Context) { contractAddress := c.Param("contract") eventSignature := c.Param("signature") - handleLogsRequest(c, contractAddress, eventSignature) + strippedSignature := common.StripPayload(eventSignature) + handleLogsRequest(c, contractAddress, strippedSignature) } func handleLogsRequest(c *gin.Context, contractAddress, signature string) { diff --git a/internal/handlers/transactions_handlers.go b/internal/handlers/transactions_handlers.go index 4ebdc28..62e4a50 100644 --- a/internal/handlers/transactions_handlers.go +++ b/internal/handlers/transactions_handlers.go @@ -7,6 +7,7 @@ import ( "github.com/gin-gonic/gin" "github.com/rs/zerolog/log" "github.com/thirdweb-dev/indexer/api" + "github.com/thirdweb-dev/indexer/internal/common" "github.com/thirdweb-dev/indexer/internal/rpc" "github.com/thirdweb-dev/indexer/internal/storage" ) @@ -107,7 +108,8 @@ func GetTransactionsByContract(c *gin.Context) { func GetTransactionsByContractAndSignature(c *gin.Context) { to := c.Param("to") signature := c.Param("signature") - handleTransactionsRequest(c, to, signature) + strippedSignature := common.StripPayload(signature) + handleTransactionsRequest(c, to, strippedSignature) } func handleTransactionsRequest(c *gin.Context, contractAddress, signature string) {