• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

unpackdev / solgo / 8465622719

28 Mar 2024 10:07AM UTC coverage: 68.339% (-0.3%) from 68.666%
8465622719

push

github

web-flow
Introducing log, tx decoder including fixes for constructor (#163)

4 of 185 new or added lines in 4 files covered. (2.16%)

2 existing lines in 1 file now uncovered.

26677 of 39036 relevant lines covered (68.34%)

0.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/bytecode/log.go
1
package bytecode
2

3
import (
4
        "bytes"
5
        "fmt"
6
        "math/big"
7
        "strings"
8

9
        abi "github.com/ethereum/go-ethereum/accounts/abi"
10
        "github.com/ethereum/go-ethereum/common"
11
        "github.com/ethereum/go-ethereum/core/types"
12
        "github.com/unpackdev/solgo/utils"
13
)
14

15
// Topic represents a single decoded topic from an Ethereum event log. Topics are attributes
16
// of an event, such as the method signature and indexed parameters.
17
type Topic struct {
18
        Name  string `json:"name"`  // The name of the topic.
19
        Value any    `json:"value"` // The value of the topic, decoded into the appropriate Go data type.
20
}
21

22
// Log encapsulates a decoded Ethereum event log. It includes the event's details such as its name,
23
// signature, the contract that emitted the event, and the decoded data and topics.
24
type Log struct {
25
        Event        *abi.Event         `json:"-"`             // ABI definition of the log's event.
26
        Address      common.Address     `json:"address"`       // Address of the contract that emitted the event.
27
        Abi          string             `json:"abi"`           // ABI string of the event.
28
        SignatureHex common.Hash        `json:"signature_hex"` // Hex-encoded signature of the event.
29
        Signature    string             `json:"signature"`     // Signature of the event.
30
        Type         utils.LogEventType `json:"type"`          // Type of the event, classified by solgo.
31
        Name         string             `json:"name"`          // Name of the event.
32
        Data         map[string]any     `json:"data"`          // Decoded event data.
33
        Topics       []Topic            `json:"topics"`        // Decoded topics of the event.
34
}
35

36
// DecodeLogFromAbi decodes an Ethereum event log using the provided ABI data. It returns a Log
37
// instance containing the decoded event name, data, and topics. The function requires the event log
38
// and its ABI as inputs. It handles errors such as missing topics or failure to parse the ABI.
NEW
39
func DecodeLogFromAbi(log *types.Log, abiData []byte) (*Log, error) {
×
NEW
40
        if log == nil || len(log.Topics) < 1 {
×
NEW
41
                return nil, fmt.Errorf("log is nil or has no topics")
×
NEW
42
        }
×
43

NEW
44
        logABI, err := abi.JSON(bytes.NewReader(abiData))
×
NEW
45
        if err != nil {
×
NEW
46
                return nil, fmt.Errorf("failed to parse abi: %s", err)
×
NEW
47
        }
×
48

NEW
49
        event, err := logABI.EventByID(log.Topics[0])
×
NEW
50
        if err != nil {
×
NEW
51
                return nil, fmt.Errorf("failed to get event by id %s: %s", log.Topics[0].Hex(), err)
×
NEW
52
        }
×
53

NEW
54
        data := make(map[string]any)
×
NEW
55
        if err := event.Inputs.UnpackIntoMap(data, log.Data); err != nil {
×
NEW
56
                return nil, fmt.Errorf("failed to unpack inputs into map: %s", err)
×
NEW
57
        }
×
58

NEW
59
        decodedTopics := make([]Topic, len(log.Topics))
×
NEW
60
        for i, topic := range log.Topics {
×
NEW
61
                if i == 0 {
×
NEW
62
                        continue
×
63
                }
64

NEW
65
                decodedTopic, err := decodeTopic(topic, event.Inputs[i-1])
×
NEW
66
                if err != nil {
×
NEW
67
                        return nil, fmt.Errorf("failed to decode topic: %s", err)
×
NEW
68
                }
×
69

NEW
70
                decodedTopics[i] = Topic{
×
NEW
71
                        Name:  event.Inputs[i-1].Name,
×
NEW
72
                        Value: decodedTopic,
×
NEW
73
                }
×
74
        }
75

NEW
76
        eventAbi, err := utils.EventToABI(event)
×
NEW
77
        if err != nil {
×
NEW
78
                return nil, fmt.Errorf("failed to cast event into the abi: %w", err)
×
NEW
79
        }
×
80

NEW
81
        toReturn := &Log{
×
NEW
82
                Event:        event,
×
NEW
83
                Address:      log.Address,
×
NEW
84
                Abi:          eventAbi,
×
NEW
85
                SignatureHex: log.Topics[0],
×
NEW
86
                Signature:    strings.TrimLeft(event.String(), "event "),
×
NEW
87
                Name:         event.Name,
×
NEW
88
                Type:         utils.LogEventType(strings.ToLower(event.Name)),
×
NEW
89
                Data:         data,
×
NEW
90
                Topics:       decodedTopics[1:], // Exclude the first topic (event signature)
×
NEW
91
        }
×
NEW
92

×
NEW
93
        return toReturn, nil
×
94
}
95

96
// decodeTopic decodes a single topic from an Ethereum event log based on its ABI argument type.
97
// It supports various data types including addresses, booleans, integers, strings, bytes, and more.
98
// This function is internal and used within DecodeLogFromAbi to process each topic individually.
NEW
99
func decodeTopic(topic common.Hash, argument abi.Argument) (interface{}, error) {
×
NEW
100
        switch argument.Type.T {
×
NEW
101
        case abi.AddressTy:
×
NEW
102
                return common.BytesToAddress(topic.Bytes()), nil
×
NEW
103
        case abi.BoolTy:
×
NEW
104
                return topic[common.HashLength-1] == 1, nil
×
NEW
105
        case abi.IntTy, abi.UintTy:
×
NEW
106
                size := argument.Type.Size
×
NEW
107
                if size > 256 {
×
NEW
108
                        return nil, fmt.Errorf("unsupported integer size: %d", size)
×
NEW
109
                }
×
NEW
110
                integer := new(big.Int).SetBytes(topic[:])
×
NEW
111
                if argument.Type.T == abi.IntTy && size < 256 {
×
NEW
112
                        integer = adjustIntSize(integer, size)
×
NEW
113
                }
×
NEW
114
                return integer, nil
×
NEW
115
        case abi.StringTy:
×
NEW
116
                return topic, nil
×
NEW
117
        case abi.BytesTy, abi.FixedBytesTy:
×
NEW
118
                return topic.Bytes(), nil
×
NEW
119
        case abi.SliceTy, abi.ArrayTy:
×
NEW
120
                return nil, fmt.Errorf("array/slice decoding not implemented")
×
NEW
121
        default:
×
NEW
122
                return nil, fmt.Errorf("decoding for type %v not implemented", argument.Type.T)
×
123
        }
124
}
125

126
// adjustIntSize adjusts the size of an integer to match its ABI-specified size, which is relevant
127
// for signed integers smaller than 256 bits. This function ensures the integer is correctly
128
// interpreted according to its defined bit size in the ABI.
NEW
129
func adjustIntSize(integer *big.Int, size int) *big.Int {
×
NEW
130
        if size == 256 || integer.Bit(size-1) == 0 {
×
NEW
131
                return integer
×
NEW
132
        }
×
NEW
133
        return new(big.Int).Sub(integer, new(big.Int).Lsh(big.NewInt(1), uint(size)))
×
134
}
135

136
// GetTopicByName searches for and returns a Topic by its name from a slice of Topic instances.
137
// It facilitates accessing specific topics directly by name rather than iterating over the slice.
138
// If the topic is not found, it returns nil.
NEW
139
func GetTopicByName(name string, topics []Topic) *Topic {
×
NEW
140
        for _, topic := range topics {
×
NEW
141
                if topic.Name == name {
×
NEW
142
                        return &topic
×
NEW
143
                }
×
144
        }
NEW
145
        return nil
×
146
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc