Skip to content

Commit 04b35e5

Browse files
Merge pull request #139 from thirdweb-dev/vt-decoding-txs-and-events
wip: decode txs using contract api
2 parents 488e3a4 + c22a7ae commit 04b35e5

File tree

6 files changed

+130
-4
lines changed

6 files changed

+130
-4
lines changed

configs/config.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,10 @@ type BasicAuthConfig struct {
109109
}
110110

111111
type APIConfig struct {
112-
Host string `mapstructure:"host"`
113-
BasicAuth BasicAuthConfig `mapstructure:"basicAuth"`
112+
Host string `mapstructure:"host"`
113+
BasicAuth BasicAuthConfig `mapstructure:"basicAuth"`
114+
ThirdwebContractApi string `mapstructure:"thirdwebContractApi"`
115+
AbiDecodingEnabled bool `mapstructure:"abiDecodingEnabled"`
114116
}
115117

116118
type Config struct {

internal/common/abi.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,45 @@ package common
22

33
import (
44
"fmt"
5+
"net/http"
56
"regexp"
67
"strings"
78

9+
config "github.com/thirdweb-dev/indexer/configs"
10+
811
"github.com/ethereum/go-ethereum/accounts/abi"
912
)
1013

14+
func GetABIForContractWithCache(chainId string, contract string, abiCache map[string]*abi.ABI) *abi.ABI {
15+
abi, ok := abiCache[contract]
16+
if !ok {
17+
abiResult, err := GetABIForContract(chainId, contract)
18+
if err != nil {
19+
abiCache[contract] = nil
20+
return nil
21+
} else {
22+
abiCache[contract] = abiResult
23+
abi = abiResult
24+
}
25+
}
26+
return abi
27+
}
28+
29+
func GetABIForContract(chainId string, contract string) (*abi.ABI, error) {
30+
url := fmt.Sprintf("%s/abi/%s/%s", config.Cfg.API.ThirdwebContractApi, chainId, contract)
31+
32+
resp, err := http.Get(url)
33+
if err != nil {
34+
return nil, fmt.Errorf("failed to get contract abi: %v", err)
35+
}
36+
37+
abi, err := abi.JSON(resp.Body)
38+
if err != nil {
39+
return nil, fmt.Errorf("failed to load contract abi: %v", err)
40+
}
41+
return &abi, nil
42+
}
43+
1144
func ConstructEventABI(signature string) (*abi.Event, error) {
1245
// Regex to extract the event name and parameters
1346
regex := regexp.MustCompile(`^(\w+)\((.*)\)$`)

internal/common/log.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/hex"
55
"fmt"
66
"math/big"
7+
"sync"
78

89
"github.com/ethereum/go-ethereum/accounts/abi"
910
gethCommon "github.com/ethereum/go-ethereum/common"
@@ -39,6 +40,41 @@ type DecodedLog struct {
3940
Decoded DecodedLogData `json:"decodedData"`
4041
}
4142

43+
func DecodeLogs(chainId string, logs []Log) []*DecodedLog {
44+
decodedLogs := make([]*DecodedLog, len(logs))
45+
abiCache := make(map[string]*abi.ABI)
46+
47+
decodeLogFunc := func(eventLog *Log) *DecodedLog {
48+
decodedLog := DecodedLog{Log: *eventLog}
49+
abi := GetABIForContractWithCache(chainId, eventLog.Address, abiCache)
50+
if abi == nil {
51+
return &decodedLog
52+
}
53+
54+
event, err := abi.EventByID(gethCommon.HexToHash(eventLog.Topics[0]))
55+
if err != nil {
56+
log.Debug().Msgf("failed to get method by id: %v", err)
57+
return &decodedLog
58+
}
59+
if event == nil {
60+
return &decodedLog
61+
}
62+
return eventLog.Decode(event)
63+
}
64+
65+
var wg sync.WaitGroup
66+
for idx, eventLog := range logs {
67+
wg.Add(1)
68+
go func(idx int, eventLog Log) {
69+
defer wg.Done()
70+
decodedLog := decodeLogFunc(&eventLog)
71+
decodedLogs[idx] = decodedLog
72+
}(idx, eventLog)
73+
}
74+
wg.Wait()
75+
return decodedLogs
76+
}
77+
4278
func (l *Log) Decode(eventABI *abi.Event) *DecodedLog {
4379

4480
decodedIndexed := make(map[string]interface{})

internal/common/transaction.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/hex"
55
"math/big"
66
"strings"
7+
"sync"
78

89
"github.com/ethereum/go-ethereum/accounts/abi"
910
"github.com/rs/zerolog/log"
@@ -52,6 +53,49 @@ type DecodedTransaction struct {
5253
Decoded DecodedTransactionData `json:"decodedData"`
5354
}
5455

56+
func DecodeTransactions(chainId string, txs []Transaction) []*DecodedTransaction {
57+
decodedTxs := make([]*DecodedTransaction, len(txs))
58+
abiCache := make(map[string]*abi.ABI)
59+
decodeTxFunc := func(transaction *Transaction) *DecodedTransaction {
60+
decodedTransaction := DecodedTransaction{Transaction: *transaction}
61+
abi := GetABIForContractWithCache(chainId, transaction.ToAddress, abiCache)
62+
if abi == nil {
63+
return &decodedTransaction
64+
}
65+
66+
decodedData, err := hex.DecodeString(strings.TrimPrefix(transaction.Data, "0x"))
67+
if err != nil {
68+
return &decodedTransaction
69+
}
70+
71+
if len(decodedData) < 4 {
72+
return &decodedTransaction
73+
}
74+
methodID := decodedData[:4]
75+
method, err := abi.MethodById(methodID)
76+
if err != nil {
77+
log.Debug().Msgf("failed to get method by id: %v", err)
78+
return &decodedTransaction
79+
}
80+
if method == nil {
81+
return &decodedTransaction
82+
}
83+
return transaction.Decode(method)
84+
}
85+
86+
var wg sync.WaitGroup
87+
for idx, transaction := range txs {
88+
wg.Add(1)
89+
go func(idx int, transaction Transaction) {
90+
defer wg.Done()
91+
decodedTx := decodeTxFunc(&transaction)
92+
decodedTxs[idx] = decodedTx
93+
}(idx, transaction)
94+
}
95+
wg.Wait()
96+
return decodedTxs
97+
}
98+
5599
func (t *Transaction) Decode(functionABI *abi.Method) *DecodedTransaction {
56100
decodedData, err := hex.DecodeString(strings.TrimPrefix(t.Data, "0x"))
57101
if err != nil {

internal/handlers/logs_handlers.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,12 @@ func handleLogsRequest(c *gin.Context, contractAddress, signature string, eventA
209209
}
210210
queryResult.Data = decodedLogs
211211
} else {
212-
queryResult.Data = logsResult.Data
212+
if config.Cfg.API.AbiDecodingEnabled {
213+
decodedLogs := common.DecodeLogs(chainId.String(), logsResult.Data)
214+
queryResult.Data = decodedLogs
215+
} else {
216+
queryResult.Data = logsResult.Data
217+
}
213218
}
214219
queryResult.Meta.TotalItems = len(logsResult.Data)
215220
}

internal/handlers/transactions_handlers.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/gin-gonic/gin"
99
"github.com/rs/zerolog/log"
1010
"github.com/thirdweb-dev/indexer/api"
11+
config "github.com/thirdweb-dev/indexer/configs"
1112
"github.com/thirdweb-dev/indexer/internal/common"
1213
"github.com/thirdweb-dev/indexer/internal/rpc"
1314
"github.com/thirdweb-dev/indexer/internal/storage"
@@ -219,7 +220,12 @@ func handleTransactionsRequest(c *gin.Context, contractAddress, signature string
219220
}
220221
queryResult.Data = decodedTransactions
221222
} else {
222-
queryResult.Data = transactionsResult.Data
223+
if config.Cfg.API.AbiDecodingEnabled {
224+
decodedTransactions := common.DecodeTransactions(chainId.String(), transactionsResult.Data)
225+
queryResult.Data = decodedTransactions
226+
} else {
227+
queryResult.Data = transactionsResult.Data
228+
}
223229
}
224230
queryResult.Meta.TotalItems = len(transactionsResult.Data)
225231
}

0 commit comments

Comments
 (0)