Skip to content

Commit

Permalink
Strip signature params (#118)
Browse files Browse the repository at this point in the history
### TL;DR

Added functionality to strip and normalize Solidity function and event signatures.

### What changed?

- Introduced a new `StripPayload` function in `utils.go` to remove parameter names, 'indexed' keywords, and extra whitespaces from Solidity function or event signatures.
- Implemented helper functions `parseParameters`, `cleanType`, and `isType` to support the signature stripping process.
- Updated `GetLogsByContractAndSignature` and `GetTransactionsByContractAndSignature` handlers to use the new `StripPayload` function before processing requests.

### How to test?

1. Call the API endpoints for getting logs or transactions by contract and signature.
2. Use various Solidity function and event signatures, including those with parameter names, `indexed` keywords, and extra whitespaces.
3. Verify that the API correctly handles and matches these signatures, regardless of the extra information included.

### Why make this change?

This change improves the flexibility and user-friendliness of the API. It allows users to query logs and transactions using more verbose Solidity signatures without requiring exact matches. This normalization process ensures that signatures with different formatting or additional information (like parameter names) are still correctly recognized and processed by the system.
  • Loading branch information
catalyst17 authored Oct 28, 2024
2 parents 22b7991 + 6f0e94e commit 6d1009b
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 3 deletions.
155 changes: 154 additions & 1 deletion internal/common/utils.go
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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]
}
4 changes: 3 additions & 1 deletion internal/handlers/logs_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/rs/zerolog/log"
"github.com/thirdweb-dev/indexer/api"
config "github.com/thirdweb-dev/indexer/configs"
"github.com/thirdweb-dev/indexer/internal/common"
"github.com/thirdweb-dev/indexer/internal/storage"
)

Expand Down Expand Up @@ -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) {
Expand Down
4 changes: 3 additions & 1 deletion internal/handlers/transactions_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 6d1009b

Please sign in to comment.