|
7 | 7 | "math/big" |
8 | 8 | "net/http" |
9 | 9 | "path/filepath" |
| 10 | + "reflect" |
10 | 11 | "strings" |
11 | 12 | "time" |
12 | 13 |
|
@@ -1167,38 +1168,144 @@ func (t TransactionLog) GetData() []byte { |
1167 | 1168 | } |
1168 | 1169 |
|
1169 | 1170 | func (m *Client) decodeContractLogs(l zerolog.Logger, logs []types.Log, allABIs []*abi.ABI) ([]DecodedTransactionLog, error) { |
1170 | | - l.Trace().Msg("Decoding ALL events") |
| 1171 | + l.Trace(). |
| 1172 | + Msg("Decoding events") |
| 1173 | + sigMap := buildEventSignatureMap(allABIs) |
| 1174 | + |
1171 | 1175 | var eventsParsed []DecodedTransactionLog |
1172 | 1176 | for _, lo := range logs { |
1173 | | - ABI_LOOP: |
1174 | | - for _, a := range allABIs { |
1175 | | - for _, evSpec := range a.Events { |
1176 | | - if evSpec.ID.Hex() == lo.Topics[0].Hex() { |
1177 | | - d := TransactionLog{lo.Topics, lo.Data} |
1178 | | - l.Trace().Str("Name", evSpec.RawName).Str("Signature", evSpec.Sig).Msg("Unpacking event") |
1179 | | - eventsMap, topicsMap, err := decodeEventFromLog(l, *a, evSpec, d) |
1180 | | - if err != nil { |
1181 | | - return nil, errors.Wrap(err, ErrDecodeLog) |
1182 | | - } |
1183 | | - parsedEvent := decodedLogFromMaps(&DecodedTransactionLog{}, eventsMap, topicsMap) |
1184 | | - decodedTransactionLog, ok := parsedEvent.(*DecodedTransactionLog) |
1185 | | - if ok { |
1186 | | - decodedTransactionLog.Signature = evSpec.Sig |
1187 | | - m.mergeLogMeta(decodedTransactionLog, lo) |
1188 | | - eventsParsed = append(eventsParsed, *decodedTransactionLog) |
1189 | | - l.Trace().Interface("Log", parsedEvent).Msg("Transaction log") |
1190 | | - break ABI_LOOP |
1191 | | - } |
1192 | | - l.Trace(). |
1193 | | - Str("Actual type", fmt.Sprintf("%T", decodedTransactionLog)). |
1194 | | - Msg("Failed to cast decoded event to DecodedCommonLog") |
| 1177 | + if len(lo.Topics) == 0 { |
| 1178 | + l.Debug(). |
| 1179 | + Msg("Log has no topics; skipping") |
| 1180 | + continue |
| 1181 | + } |
| 1182 | + |
| 1183 | + eventSig := lo.Topics[0].Hex() |
| 1184 | + possibleEvents, exists := sigMap[eventSig] |
| 1185 | + if !exists { |
| 1186 | + l.Trace(). |
| 1187 | + Str("Event signature", eventSig). |
| 1188 | + Msg("No matching events found for signature") |
| 1189 | + continue |
| 1190 | + } |
| 1191 | + |
| 1192 | + // Check if we know what contract is this log from and if we do, get its ABI to skip unnecessary iterations |
| 1193 | + var knownContractABI *abi.ABI |
| 1194 | + if contractName := m.ContractAddressToNameMap.GetContractName(lo.Address.Hex()); contractName != "" { |
| 1195 | + maybeABI, ok := m.ContractStore.GetABI(contractName) |
| 1196 | + if !ok { |
| 1197 | + l.Trace(). |
| 1198 | + Str("Event signature", eventSig). |
| 1199 | + Str("Contract name", contractName). |
| 1200 | + Str("Contract address", lo.Address.Hex()). |
| 1201 | + Msg("No ABI found for known contract; this is unexpected. Continuing with step-by-step ABI search") |
| 1202 | + } else { |
| 1203 | + knownContractABI = maybeABI |
| 1204 | + } |
| 1205 | + } |
| 1206 | + |
| 1207 | + // Iterate over possible events with the same signature |
| 1208 | + matched := false |
| 1209 | + for _, evWithABI := range possibleEvents { |
| 1210 | + evSpec := evWithABI.EventSpec |
| 1211 | + contractABI := evWithABI.ContractABI |
| 1212 | + |
| 1213 | + // Check if known contract ABI matches candidate ABI and if not, skip this ABI and try the next one |
| 1214 | + if knownContractABI != nil && !reflect.DeepEqual(knownContractABI, contractABI) { |
| 1215 | + l.Trace(). |
| 1216 | + Str("Event signature", eventSig). |
| 1217 | + Str("Contract address", lo.Address.Hex()). |
| 1218 | + Msg("ABI doesn't match known ABI for this address; trying next ABI") |
| 1219 | + continue |
| 1220 | + } |
| 1221 | + |
| 1222 | + // Validate indexed parameters count |
| 1223 | + // Non-indexed parameters are stored in the Data field, |
| 1224 | + // and much harder to validate due to dynamic types, |
| 1225 | + // so we skip them for now |
| 1226 | + var indexedParams abi.Arguments |
| 1227 | + for _, input := range evSpec.Inputs { |
| 1228 | + if input.Indexed { |
| 1229 | + indexedParams = append(indexedParams, input) |
1195 | 1230 | } |
1196 | 1231 | } |
| 1232 | + |
| 1233 | + expectedIndexed := len(indexedParams) |
| 1234 | + actualIndexed := len(lo.Topics) - 1 // First topic is the event signature |
| 1235 | + |
| 1236 | + if expectedIndexed != actualIndexed { |
| 1237 | + l.Trace(). |
| 1238 | + Str("Event", evSpec.Name). |
| 1239 | + Int("Expected indexed param count", expectedIndexed). |
| 1240 | + Int("Actual indexed param count", actualIndexed). |
| 1241 | + Msg("Mismatch in indexed parameters; skipping event") |
| 1242 | + continue |
| 1243 | + } |
| 1244 | + |
| 1245 | + // Proceed to decode the event |
| 1246 | + d := TransactionLog{lo.Topics, lo.Data} |
| 1247 | + l.Trace(). |
| 1248 | + Str("Name", evSpec.RawName). |
| 1249 | + Str("Signature", evSpec.Sig). |
| 1250 | + Msg("Unpacking event") |
| 1251 | + |
| 1252 | + eventsMap, topicsMap, err := decodeEventFromLog(l, *contractABI, *evSpec, d) |
| 1253 | + if err != nil { |
| 1254 | + l.Error(). |
| 1255 | + Err(err). |
| 1256 | + Str("Event", evSpec.Name). |
| 1257 | + Msg("Failed to decode event; skipping") |
| 1258 | + continue // Skip this event instead of returning an error |
| 1259 | + } |
| 1260 | + |
| 1261 | + parsedEvent := decodedLogFromMaps(&DecodedTransactionLog{}, eventsMap, topicsMap) |
| 1262 | + decodedTransactionLog, ok := parsedEvent.(*DecodedTransactionLog) |
| 1263 | + if ok { |
| 1264 | + decodedTransactionLog.Signature = evSpec.Sig |
| 1265 | + m.mergeLogMeta(decodedTransactionLog, lo) |
| 1266 | + eventsParsed = append(eventsParsed, *decodedTransactionLog) |
| 1267 | + l.Trace(). |
| 1268 | + Interface("Log", parsedEvent). |
| 1269 | + Msg("Transaction log decoded successfully") |
| 1270 | + matched = true |
| 1271 | + break // Move to the next log after successful decoding |
| 1272 | + } |
| 1273 | + |
| 1274 | + l.Trace(). |
| 1275 | + Str("Actual type", fmt.Sprintf("%T", decodedTransactionLog)). |
| 1276 | + Msg("Failed to cast decoded event to DecodedTransactionLog") |
| 1277 | + } |
| 1278 | + |
| 1279 | + if !matched { |
| 1280 | + l.Warn(). |
| 1281 | + Str("Signature", eventSig). |
| 1282 | + Msg("No matching event with valid indexed parameter count found for log") |
1197 | 1283 | } |
1198 | 1284 | } |
1199 | 1285 | return eventsParsed, nil |
1200 | 1286 | } |
1201 | 1287 |
|
| 1288 | +type eventWithABI struct { |
| 1289 | + ContractABI *abi.ABI |
| 1290 | + EventSpec *abi.Event |
| 1291 | +} |
| 1292 | + |
| 1293 | +// buildEventSignatureMap precomputes a mapping from event signature to events with their ABIs |
| 1294 | +func buildEventSignatureMap(allABIs []*abi.ABI) map[string][]*eventWithABI { |
| 1295 | + sigMap := make(map[string][]*eventWithABI) |
| 1296 | + for _, a := range allABIs { |
| 1297 | + for _, ev := range a.Events { |
| 1298 | + event := ev //nolint:copyloopvar // Explicitly keeping the copy for clarity |
| 1299 | + sigMap[ev.ID.Hex()] = append(sigMap[ev.ID.Hex()], &eventWithABI{ |
| 1300 | + ContractABI: a, |
| 1301 | + EventSpec: &event, |
| 1302 | + }) |
| 1303 | + } |
| 1304 | + } |
| 1305 | + |
| 1306 | + return sigMap |
| 1307 | +} |
| 1308 | + |
1202 | 1309 | // WaitUntilNoPendingTxForRootKey waits until there's no pending transaction for root key. If after timeout there are still pending transactions, it returns error. |
1203 | 1310 | func (m *Client) WaitUntilNoPendingTxForRootKey(timeout time.Duration) error { |
1204 | 1311 | return m.WaitUntilNoPendingTx(m.MustGetRootKeyAddress(), timeout) |
|
0 commit comments