// Moved here because transactions package depends on accounts package which // depends on appdatabase where this functionality is needed package common import ( "encoding/binary" "fmt" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" ) // Type type of transaction type Type string // Log Event type type EventType string const ( // Transaction types EthTransfer Type = "eth" Erc20Transfer Type = "erc20" Erc721Transfer Type = "erc721" Erc1155Transfer Type = "erc1155" UniswapV2Swap Type = "uniswapV2Swap" UniswapV3Swap Type = "uniswapV3Swap" HopBridgeFrom Type = "HopBridgeFrom" HopBridgeTo Type = "HopBridgeTo" unknownTransaction Type = "unknown" // Event types WETHDepositEventType EventType = "wethDepositEvent" WETHWithdrawalEventType EventType = "wethWithdrawalEvent" Erc20TransferEventType EventType = "erc20Event" Erc721TransferEventType EventType = "erc721Event" Erc1155TransferSingleEventType EventType = "erc1155SingleEvent" Erc1155TransferBatchEventType EventType = "erc1155BatchEvent" UniswapV2SwapEventType EventType = "uniswapV2SwapEvent" UniswapV3SwapEventType EventType = "uniswapV3SwapEvent" HopBridgeTransferSentToL2EventType EventType = "hopBridgeTransferSentToL2Event" HopBridgeTransferFromL1CompletedEventType EventType = "hopBridgeTransferFromL1CompletedEvent" HopBridgeWithdrawalBondedEventType EventType = "hopBridgeWithdrawalBondedEvent" HopBridgeTransferSentEventType EventType = "hopBridgeTransferSentEvent" UnknownEventType EventType = "unknownEvent" // Deposit (index_topic_1 address dst, uint256 wad) wethDepositEventSignature = "Deposit(address,uint256)" // Withdrawal (index_topic_1 address src, uint256 wad) wethWithdrawalEventSignature = "Withdrawal(address,uint256)" // Transfer (index_topic_1 address from, index_topic_2 address to, uint256 value) // Transfer (index_topic_1 address from, index_topic_2 address to, index_topic_3 uint256 tokenId) Erc20_721TransferEventSignature = "Transfer(address,address,uint256)" Erc1155TransferSingleEventSignature = "TransferSingle(address,address,address,uint256,uint256)" // operator, from, to, id, value Erc1155TransferBatchEventSignature = "TransferBatch(address,address,address,uint256[],uint256[])" // operator, from, to, ids, values erc20TransferEventIndexedParameters = 3 // signature, from, to erc721TransferEventIndexedParameters = 4 // signature, from, to, tokenId erc1155TransferEventIndexedParameters = 4 // signature, operator, from, to (id, value are not indexed) // Swap (index_topic_1 address sender, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, index_topic_2 address to) uniswapV2SwapEventSignature = "Swap(address,uint256,uint256,uint256,uint256,address)" // also used by SushiSwap // Swap (index_topic_1 address sender, index_topic_2 address recipient, int256 amount0, int256 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick) uniswapV3SwapEventSignature = "Swap(address,address,int256,int256,uint160,uint128,int24)" // TransferSentToL2 (index_topic_1 uint256 chainId, index_topic_2 address recipient, uint256 amount, uint256 amountOutMin, uint256 deadline, index_topic_3 address relayer, uint256 relayerFee) hopBridgeTransferSentToL2EventSignature = "TransferSentToL2(uint256,address,uint256,uint256,uint256,address,uint256)" // TransferFromL1Completed (index_topic_1 address recipient, uint256 amount, uint256 amountOutMin, uint256 deadline, index_topic_2 address relayer, uint256 relayerFee) HopBridgeTransferFromL1CompletedEventSignature = "TransferFromL1Completed(address,uint256,uint256,uint256,address,uint256)" // WithdrawalBonded (index_topic_1 bytes32 transferID, uint256 amount) hopBridgeWithdrawalBondedEventSignature = "WithdrawalBonded(bytes32,uint256)" // TransferSent (index_topic_1 bytes32 transferID, index_topic_2 uint256 chainId, index_topic_3 address recipient, uint256 amount, bytes32 transferNonce, uint256 bonderFee, uint256 index, uint256 amountOutMin, uint256 deadline) hopBridgeTransferSentEventSignature = "TransferSent(bytes32,uint256,address,uint256,bytes32,uint256,uint256,uint256,uint256)" ) var ( // MaxUint256 is the maximum value that can be represented by a uint256. MaxUint256 = new(big.Int).Sub(new(big.Int).Lsh(common.Big1, 256), common.Big1) ) // Detect event type for a cetain item from the Events Log func GetEventType(log *types.Log) EventType { wethDepositEventSignatureHash := GetEventSignatureHash(wethDepositEventSignature) wethWithdrawalEventSignatureHash := GetEventSignatureHash(wethWithdrawalEventSignature) erc20_721TransferEventSignatureHash := GetEventSignatureHash(Erc20_721TransferEventSignature) erc1155TransferSingleEventSignatureHash := GetEventSignatureHash(Erc1155TransferSingleEventSignature) erc1155TransferBatchEventSignatureHash := GetEventSignatureHash(Erc1155TransferBatchEventSignature) uniswapV2SwapEventSignatureHash := GetEventSignatureHash(uniswapV2SwapEventSignature) uniswapV3SwapEventSignatureHash := GetEventSignatureHash(uniswapV3SwapEventSignature) hopBridgeTransferSentToL2EventSignatureHash := GetEventSignatureHash(hopBridgeTransferSentToL2EventSignature) hopBridgeTransferFromL1CompletedEventSignatureHash := GetEventSignatureHash(HopBridgeTransferFromL1CompletedEventSignature) hopBridgeWithdrawalBondedEventSignatureHash := GetEventSignatureHash(hopBridgeWithdrawalBondedEventSignature) hopBridgeTransferSentEventSignatureHash := GetEventSignatureHash(hopBridgeTransferSentEventSignature) if len(log.Topics) > 0 { switch log.Topics[0] { case wethDepositEventSignatureHash: return WETHDepositEventType case wethWithdrawalEventSignatureHash: return WETHWithdrawalEventType case erc20_721TransferEventSignatureHash: switch len(log.Topics) { case erc20TransferEventIndexedParameters: return Erc20TransferEventType case erc721TransferEventIndexedParameters: return Erc721TransferEventType } case erc1155TransferSingleEventSignatureHash: return Erc1155TransferSingleEventType case erc1155TransferBatchEventSignatureHash: return Erc1155TransferBatchEventType case uniswapV2SwapEventSignatureHash: return UniswapV2SwapEventType case uniswapV3SwapEventSignatureHash: return UniswapV3SwapEventType case hopBridgeTransferSentToL2EventSignatureHash: return HopBridgeTransferSentToL2EventType case hopBridgeTransferFromL1CompletedEventSignatureHash: return HopBridgeTransferFromL1CompletedEventType case hopBridgeWithdrawalBondedEventSignatureHash: return HopBridgeWithdrawalBondedEventType case hopBridgeTransferSentEventSignatureHash: return HopBridgeTransferSentEventType } } return UnknownEventType } func EventTypeToSubtransactionType(eventType EventType) Type { switch eventType { case Erc20TransferEventType: return Erc20Transfer case Erc721TransferEventType: return Erc721Transfer case Erc1155TransferSingleEventType, Erc1155TransferBatchEventType: return Erc1155Transfer case UniswapV2SwapEventType: return UniswapV2Swap case UniswapV3SwapEventType: return UniswapV3Swap case HopBridgeTransferSentToL2EventType, HopBridgeTransferSentEventType: return HopBridgeFrom case HopBridgeTransferFromL1CompletedEventType, HopBridgeWithdrawalBondedEventType: return HopBridgeTo } return unknownTransaction } func GetFirstEvent(logs []*types.Log) (EventType, *types.Log) { for _, log := range logs { eventType := GetEventType(log) if eventType != UnknownEventType { return eventType, log } } return UnknownEventType, nil } func IsTokenTransfer(logs []*types.Log) bool { eventType, _ := GetFirstEvent(logs) switch eventType { case Erc20TransferEventType, Erc721TransferEventType, Erc1155TransferSingleEventType, Erc1155TransferBatchEventType: return true } return false } func ParseWETHDepositLog(ethlog *types.Log) (src common.Address, amount *big.Int) { amount = new(big.Int) if len(ethlog.Topics) < 2 { log.Warn("not enough topics for WETH deposit", "topics", ethlog.Topics) return } if len(ethlog.Topics[1]) != 32 { log.Warn("second topic is not padded to 32 byte address", "topic", ethlog.Topics[1]) return } copy(src[:], ethlog.Topics[1][12:]) if len(ethlog.Data) != 32 { log.Warn("data is not padded to 32 byte big int", "data", ethlog.Data) return } amount.SetBytes(ethlog.Data) return } func ParseWETHWithdrawLog(ethlog *types.Log) (dst common.Address, amount *big.Int) { amount = new(big.Int) if len(ethlog.Topics) < 2 { log.Warn("not enough topics for WETH withdraw", "topics", ethlog.Topics) return } if len(ethlog.Topics[1]) != 32 { log.Warn("second topic is not padded to 32 byte address", "topic", ethlog.Topics[1]) return } copy(dst[:], ethlog.Topics[1][12:]) if len(ethlog.Data) != 32 { log.Warn("data is not padded to 32 byte big int", "data", ethlog.Data) return } amount.SetBytes(ethlog.Data) return } func ParseErc20TransferLog(ethlog *types.Log) (from, to common.Address, amount *big.Int) { amount = new(big.Int) if len(ethlog.Topics) < erc20TransferEventIndexedParameters { log.Warn("not enough topics for erc20 transfer", "topics", ethlog.Topics) return } var err error from, to, err = getFromToAddresses(*ethlog) if err != nil { log.Error("log_parser::ParseErc20TransferLog", err) return } if len(ethlog.Data) != 32 { log.Warn("data is not padded to 32 byts big int", "data", ethlog.Data) return } amount.SetBytes(ethlog.Data) return } func ParseErc721TransferLog(ethlog *types.Log) (from, to common.Address, tokenID *big.Int) { tokenID = new(big.Int) if len(ethlog.Topics) < erc721TransferEventIndexedParameters { log.Warn("not enough topics for erc721 transfer", "topics", ethlog.Topics) return } var err error from, to, err = getFromToAddresses(*ethlog) if err != nil { log.Error("log_parser::ParseErc721TransferLog", err) return } tokenID.SetBytes(ethlog.Topics[3][:]) return } func GetLogSubTxID(log types.Log) common.Hash { // Get unique ID by using TxHash and log index index := [4]byte{} binary.BigEndian.PutUint32(index[:], uint32(log.Index)) return crypto.Keccak256Hash(log.TxHash.Bytes(), index[:]) } func getLogSubTxIDWithTokenIDIndex(log types.Log, tokenIDIdx uint16) common.Hash { // Get unique ID by using TxHash, log index and extra bytes (token id index for ERC1155 TransferBatch) index := [4]byte{} value := uint32(log.Index&0x0000FFFF) | (uint32(tokenIDIdx) << 16) // log index should not exceed uint16 max value binary.BigEndian.PutUint32(index[:], value) return crypto.Keccak256Hash(log.TxHash.Bytes(), index[:]) } func checkTopicsLength(ethlog types.Log, startIdx, endIdx int) (err error) { for i := startIdx; i < endIdx; i++ { if len(ethlog.Topics[i]) != common.HashLength { err = fmt.Errorf("topic %d is not padded to %d byte address, topic=%s", i, common.HashLength, ethlog.Topics[i]) log.Error("log_parser::checkTopicsLength", err) return } } return } func getFromToAddresses(ethlog types.Log) (from, to common.Address, err error) { eventType := GetEventType(ðlog) addressIdx := common.HashLength - common.AddressLength switch eventType { case Erc1155TransferSingleEventType, Erc1155TransferBatchEventType: err = checkTopicsLength(ethlog, 2, 4) if err != nil { return } copy(from[:], ethlog.Topics[2][addressIdx:]) copy(to[:], ethlog.Topics[3][addressIdx:]) return case Erc20TransferEventType, Erc721TransferEventType, UniswapV2SwapEventType, UniswapV3SwapEventType, HopBridgeTransferFromL1CompletedEventType: err = checkTopicsLength(ethlog, 1, 3) if err != nil { return } copy(from[:], ethlog.Topics[1][addressIdx:]) copy(to[:], ethlog.Topics[2][addressIdx:]) return } return from, to, fmt.Errorf("unsupported event type to get from/to adddresses %s", eventType) } func ParseTransferLog(ethlog types.Log) (from, to common.Address, txIDs []common.Hash, tokenIDs, values []*big.Int, err error) { eventType := GetEventType(ðlog) switch eventType { case Erc20TransferEventType: var amount *big.Int from, to, amount = ParseErc20TransferLog(ðlog) txIDs = append(txIDs, GetLogSubTxID(ethlog)) values = append(values, amount) return case Erc721TransferEventType: var tokenID *big.Int from, to, tokenID = ParseErc721TransferLog(ðlog) txIDs = append(txIDs, GetLogSubTxID(ethlog)) tokenIDs = append(tokenIDs, tokenID) values = append(values, big.NewInt(1)) return case Erc1155TransferSingleEventType, Erc1155TransferBatchEventType: _, from, to, tokenIDs, values, err = ParseErc1155TransferLog(ðlog, eventType) for i := range tokenIDs { txIDs = append(txIDs, getLogSubTxIDWithTokenIDIndex(ethlog, uint16(i))) } return } return from, to, txIDs, tokenIDs, values, fmt.Errorf("unsupported event type in log_parser::ParseTransferLogs %s", eventType) } func ParseErc1155TransferLog(ethlog *types.Log, evType EventType) (operator, from, to common.Address, ids, amounts []*big.Int, err error) { if len(ethlog.Topics) < erc1155TransferEventIndexedParameters { err = fmt.Errorf("not enough topics for erc1155 transfer %s, %v", "topics", ethlog.Topics) log.Error("log_parser::ParseErc1155TransferLog", "err", err) return } err = checkTopicsLength(*ethlog, 1, erc1155TransferEventIndexedParameters) if err != nil { return } addressIdx := common.HashLength - common.AddressLength copy(operator[:], ethlog.Topics[1][addressIdx:]) from, to, err = getFromToAddresses(*ethlog) if err != nil { log.Error("log_parser::ParseErc1155TransferLog", "err", err) return } if len(ethlog.Data) == 0 || len(ethlog.Data)%(common.HashLength*2) != 0 { err = fmt.Errorf("data is not padded to 64 bytes %s, %v", "data", ethlog.Data) log.Error("log_parser::ParseErc1155TransferLog", "err", err) return } if evType == Erc1155TransferSingleEventType { ids = append(ids, new(big.Int).SetBytes(ethlog.Data[:common.HashLength])) amounts = append(amounts, new(big.Int).SetBytes(ethlog.Data[common.HashLength:])) log.Debug("log_parser::ParseErc1155TransferSingleLog", "ids", ids, "amounts", amounts) } else { // idTypeSize := new(big.Int).SetBytes(ethlog.Data[:common.HashLength]).Uint64() // Left for knowledge // valueTypeSize := new(big.Int).SetBytes(ethlog.Data[common.HashLength : common.HashLength*2]).Uint64() // Left for knowledge idsArraySize := new(big.Int).SetBytes(ethlog.Data[common.HashLength*2 : common.HashLength*2+common.HashLength]).Uint64() initialOffset := common.HashLength*2 + common.HashLength for i := 0; i < int(idsArraySize); i++ { ids = append(ids, new(big.Int).SetBytes(ethlog.Data[initialOffset+i*common.HashLength:initialOffset+(i+1)*common.HashLength])) } valuesArraySize := new(big.Int).SetBytes(ethlog.Data[initialOffset+int(idsArraySize)*common.HashLength : initialOffset+int(idsArraySize+1)*common.HashLength]).Uint64() if idsArraySize != valuesArraySize { err = fmt.Errorf("ids and values sizes don't match %d, %d", idsArraySize, valuesArraySize) log.Error("log_parser::ParseErc1155TransferBatchLog", "err", err) return } initialOffset = initialOffset + int(idsArraySize+1)*common.HashLength for i := 0; i < int(valuesArraySize); i++ { amounts = append(amounts, new(big.Int).SetBytes(ethlog.Data[initialOffset+i*common.HashLength:initialOffset+(i+1)*common.HashLength])) log.Debug("log_parser::ParseErc1155TransferBatchLog", "id", ids[i], "amount", amounts[i]) } } return } func ParseUniswapV2Log(ethlog *types.Log) (pairAddress common.Address, from common.Address, to common.Address, amount0In *big.Int, amount1In *big.Int, amount0Out *big.Int, amount1Out *big.Int, err error) { amount0In = new(big.Int) amount1In = new(big.Int) amount0Out = new(big.Int) amount1Out = new(big.Int) if len(ethlog.Topics) < 3 { err = fmt.Errorf("not enough topics for uniswapV2 swap %s, %v", "topics", ethlog.Topics) return } pairAddress = ethlog.Address from, to, err = getFromToAddresses(*ethlog) if err != nil { log.Error("log_parser::ParseUniswapV2Log", err) return } if len(ethlog.Data) != 32*4 { err = fmt.Errorf("data is not padded to 4 * 32 bytes big int %s, %v", "data", ethlog.Data) return } amount0In.SetBytes(ethlog.Data[0:32]) amount1In.SetBytes(ethlog.Data[32:64]) amount0Out.SetBytes(ethlog.Data[64:96]) amount1Out.SetBytes(ethlog.Data[96:128]) return } func readInt256(b []byte) *big.Int { // big.SetBytes can't tell if a number is negative or positive in itself. // On EVM, if the returned number > max int256, it is negative. // A number is > max int256 if the bit at position 255 is set. ret := new(big.Int).SetBytes(b) if ret.Bit(255) == 1 { ret.Add(MaxUint256, new(big.Int).Neg(ret)) ret.Add(ret, common.Big1) ret.Neg(ret) } return ret } func ParseUniswapV3Log(ethlog *types.Log) (poolAddress common.Address, sender common.Address, recipient common.Address, amount0 *big.Int, amount1 *big.Int, err error) { amount0 = new(big.Int) amount1 = new(big.Int) if len(ethlog.Topics) < 3 { err = fmt.Errorf("not enough topics for uniswapV3 swap %s, %v", "topics", ethlog.Topics) return } poolAddress = ethlog.Address sender, recipient, err = getFromToAddresses(*ethlog) if err != nil { log.Error("log_parser::ParseUniswapV3Log", err) return } if len(ethlog.Data) != 32*5 { err = fmt.Errorf("data is not padded to 5 * 32 bytes big int %s, %v", "data", ethlog.Data) return } amount0 = readInt256(ethlog.Data[0:32]) amount1 = readInt256(ethlog.Data[32:64]) return } func ParseHopBridgeTransferSentToL2Log(ethlog *types.Log) (chainID uint64, recipient common.Address, relayer common.Address, amount *big.Int, err error) { chainIDInt := new(big.Int) amount = new(big.Int) if len(ethlog.Topics) < 4 { err = fmt.Errorf("not enough topics for HopBridgeTransferSentToL2 event %s, %v", "topics", ethlog.Topics) return } if len(ethlog.Topics[1]) != 32 { err = fmt.Errorf("second topic is not padded to 32 byte address %s, %v", "topic", ethlog.Topics[1]) return } chainIDInt.SetBytes(ethlog.Topics[1][:]) chainID = chainIDInt.Uint64() if len(ethlog.Topics[2]) != 32 { err = fmt.Errorf("third topic is not padded to 32 byte address %s, %v", "topic", ethlog.Topics[2]) return } copy(recipient[:], ethlog.Topics[2][12:]) if len(ethlog.Topics[3]) != 32 { err = fmt.Errorf("fourth topic is not padded to 32 byte address %s, %v", "topic", ethlog.Topics[3]) return } copy(relayer[:], ethlog.Topics[3][12:]) if len(ethlog.Data) != 32*4 { err = fmt.Errorf("data is not padded to 4 * 32 bytes big int %s, %v", "data", ethlog.Data) return } amount.SetBytes(ethlog.Data[0:32]) return } func ParseHopBridgeTransferFromL1CompletedLog(ethlog *types.Log) (recipient common.Address, relayer common.Address, amount *big.Int, err error) { amount = new(big.Int) if len(ethlog.Topics) < 3 { err = fmt.Errorf("not enough topics for HopBridgeTransferFromL1Completed event %s, %v", "topics", ethlog.Topics) return } recipient, relayer, err = getFromToAddresses(*ethlog) if err != nil { log.Error("log_parser::ParseHopBridgeTransferFromL1CompletedLog", err) return } if len(ethlog.Data) != 32*4 { err = fmt.Errorf("data is not padded to 4 * 32 bytes big int %s, %v", "data", ethlog.Data) return } amount.SetBytes(ethlog.Data[0:32]) return } func ParseHopWithdrawalBondedLog(ethlog *types.Log) (transferID *big.Int, amount *big.Int, err error) { transferID = new(big.Int) amount = new(big.Int) if len(ethlog.Topics) < 2 { err = fmt.Errorf("not enough topics for HopWithdrawalBonded event %s, %v", "topics", ethlog.Topics) return } if len(ethlog.Topics[1]) != 32 { err = fmt.Errorf("second topic is not padded to 32 byte address %s, %v", "topic", ethlog.Topics[1]) return } transferID.SetBytes(ethlog.Topics[1][:]) if len(ethlog.Data) != 32*1 { err = fmt.Errorf("data is not padded to 1 * 32 bytes big int %s, %v", "data", ethlog.Data) return } amount.SetBytes(ethlog.Data[0:32]) return } func ParseHopBridgeTransferSentLog(ethlog *types.Log) (transferID *big.Int, chainID uint64, recipient common.Address, amount *big.Int, transferNonce *big.Int, bonderFee *big.Int, index *big.Int, amountOutMin *big.Int, deadline *big.Int, err error) { transferID = new(big.Int) chainIDInt := new(big.Int) amount = new(big.Int) transferNonce = new(big.Int) bonderFee = new(big.Int) index = new(big.Int) amountOutMin = new(big.Int) deadline = new(big.Int) if len(ethlog.Topics) < 4 { err = fmt.Errorf("not enough topics for HopBridgeTransferSent event %s, %v", "topics", ethlog.Topics) return } if len(ethlog.Topics[1]) != 32 { err = fmt.Errorf("second topic is not padded to 32 byte big int %s, %v", "topic", ethlog.Topics[1]) return } transferID.SetBytes(ethlog.Topics[1][:]) if len(ethlog.Topics[2]) != 32 { err = fmt.Errorf("third topic is not padded to 32 byte big int %s, %v", "topic", ethlog.Topics[2]) return } chainIDInt.SetBytes(ethlog.Topics[2][:]) chainID = chainIDInt.Uint64() if len(ethlog.Topics[3]) != 32 { err = fmt.Errorf("fourth topic is not padded to 32 byte address %s, %v", "topic", ethlog.Topics[3]) return } copy(recipient[:], ethlog.Topics[2][12:]) if len(ethlog.Data) != 32*6 { err = fmt.Errorf("data is not padded to 6 * 32 bytes big int %s, %v", "data", ethlog.Data) return } amount.SetBytes(ethlog.Data[0:32]) transferNonce.SetBytes(ethlog.Data[32:64]) bonderFee.SetBytes(ethlog.Data[64:96]) index.SetBytes(ethlog.Data[96:128]) amountOutMin.SetBytes(ethlog.Data[128:160]) deadline.SetBytes(ethlog.Data[160:192]) return } func GetEventSignatureHash(signature string) common.Hash { return crypto.Keccak256Hash([]byte(signature)) } func ExtractTokenTransferData(dbEntryType Type, log *types.Log, tx *types.Transaction) (correctType Type, tokenAddress *common.Address, txFrom *common.Address, txTo *common.Address) { // erc721 transfers share signature with erc20 ones, so they both used to be categorized as erc20 // by the Downloader. We fix this here since they might be mis-categorized in the db. if dbEntryType == Erc20Transfer { eventType := GetEventType(log) correctType = EventTypeToSubtransactionType(eventType) } else { correctType = dbEntryType } switch correctType { case Erc20Transfer: tokenAddress = new(common.Address) *tokenAddress = log.Address from, to, _ := ParseErc20TransferLog(log) txFrom = &from txTo = &to case Erc721Transfer: tokenAddress = new(common.Address) *tokenAddress = log.Address from, to, _ := ParseErc721TransferLog(log) txFrom = &from txTo = &to case Erc1155Transfer: tokenAddress = new(common.Address) *tokenAddress = log.Address _, from, to, _, _, err := ParseErc1155TransferLog(log, Erc1155TransferSingleEventType) // from/to extraction is the same for single and batch if err != nil { return } txFrom = &from txTo = &to } return } func TxDataContainsAddress(txType uint8, txData []byte, address common.Address) bool { // First 4 bytes are related to the methodID const methodIDLen int = 4 const paramLen int = 32 var paramOffset int = 0 switch txType { case types.OptimismDepositTxType: // Offset for relayMessage data. // I actually don't know what the 2x32 + 4 bytes mean, but it seems to be constant in all transactions I've // checked. Will update the comment when I find out more about it. paramOffset = 5*32 + 2*32 + 4 } // Check if address is contained in any 32-byte parameter for paramStart := methodIDLen + paramOffset; paramStart < len(txData); paramStart += paramLen { paramEnd := paramStart + paramLen if paramEnd > len(txData) { break } // Address bytes should be in the last addressLen positions paramBytes := txData[paramStart:paramEnd] paramAddress := common.BytesToAddress(paramBytes) if address == paramAddress { return true } } return false }