From 54eef169e838e3bb167c63def063efea9dbc386d Mon Sep 17 00:00:00 2001 From: Alexander Cristurean Date: Mon, 8 Jul 2024 17:11:37 +0300 Subject: [PATCH] removed transaction metadata. --- process/resultsParser/errors.go | 12 + process/resultsParser/resultsParser.go | 81 ++-- process/resultsParser/resultsParser_test.go | 71 +++ process/resultsParser/returnCode.go | 17 +- .../transactionDecoder/errors.go | 28 -- .../transactionDecoder/metadata.go | 40 -- .../transactionDecoder/transactionDecoder.go | 413 ------------------ .../transactionDecoder_test.go | 199 --------- 8 files changed, 124 insertions(+), 737 deletions(-) create mode 100644 process/resultsParser/errors.go delete mode 100644 process/resultsParser/transactionDecoder/errors.go delete mode 100644 process/resultsParser/transactionDecoder/metadata.go delete mode 100644 process/resultsParser/transactionDecoder/transactionDecoder.go delete mode 100644 process/resultsParser/transactionDecoder/transactionDecoder_test.go diff --git a/process/resultsParser/errors.go b/process/resultsParser/errors.go new file mode 100644 index 00000000..c6553a54 --- /dev/null +++ b/process/resultsParser/errors.go @@ -0,0 +1,12 @@ +package resultsParser + +import ( + "errors" +) + +var ( + ErrNoReturnCode = errors.New("no return code") + ErrEmptyDataField = errors.New("empty data field") + ErrFoundMoreThanOneEvent = errors.New("found more than one event") + ErrCannotProcessDataField = errors.New("cannot process data field") +) diff --git a/process/resultsParser/resultsParser.go b/process/resultsParser/resultsParser.go index 6a1b071e..996aa24e 100644 --- a/process/resultsParser/resultsParser.go +++ b/process/resultsParser/resultsParser.go @@ -4,15 +4,12 @@ import ( "bytes" "encoding/base64" "encoding/hex" - "errors" "fmt" "strings" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data/transaction" logger "github.com/multiversx/mx-chain-logger-go" - - "github.com/multiversx/mx-chain-proxy-go/process/resultsParser/transactionDecoder" ) var log = logger.GetOrCreate("api/gin") @@ -26,34 +23,24 @@ type ResultOutcome struct { // ParseResultOutcome will try to translate the smart contract results into a ResultOutcome object. func ParseResultOutcome(tx *transaction.ApiTransactionResult, pubKeyConverter core.PubkeyConverter) (*ResultOutcome, error) { - metadata, err := transactionDecoder.GetTransactionMetadata(&transactionDecoder.TransactionToDecode{ - Sender: tx.Sender, - Receiver: tx.Receiver, - Data: tx.Data, - Value: tx.Value, - }, pubKeyConverter) - if err != nil { - return nil, fmt.Errorf("failed to retrieve transaction metadata: %w", err) - } - outcome := parseOutcomeOnSimpleMoveBalance(tx) if outcome != nil { - log.Trace("result outcome on simple move balance") + log.Trace("txHash [%s] result outcome on simple move balance", tx.Hash) return outcome, nil } outcome = parseOutcomeOnInvalidTransaction(tx) if outcome != nil { - log.Trace("result outcome on invalid transaction") + log.Trace("txHash [%s] result outcome on invalid transaction", tx.Hash) return outcome, nil } - outcome, err = parseOutcomeOnEasilyFoundResultWithReturnData(tx.SmartContractResults) + outcome, err := parseOutcomeOnEasilyFoundResultWithReturnData(tx.SmartContractResults) if err != nil { return nil, fmt.Errorf("failed to parse outcome on easily found result with return data: %w", err) } if outcome != nil { - log.Trace("result outcome on easily found result with return data") + log.Trace("txHash [%s] result outcome on easily found result with return data", tx.Hash) return outcome, nil } @@ -62,7 +49,7 @@ func ParseResultOutcome(tx *transaction.ApiTransactionResult, pubKeyConverter co return nil, fmt.Errorf("failed to parse outcome on signal error: %w", err) } if outcome != nil { - log.Trace("result outcome on signal error") + log.Trace("txHash [%s] result outcome on signal error", tx.Hash) return outcome, nil } @@ -71,7 +58,7 @@ func ParseResultOutcome(tx *transaction.ApiTransactionResult, pubKeyConverter co return nil, fmt.Errorf("failed to parse outcome on too much gas warning: %w", err) } if outcome != nil { - log.Trace("result outcome on too much gas warning") + log.Trace("txHash [%s] result outcome on too much gas warning", tx.Hash) return outcome, nil } @@ -80,14 +67,13 @@ func ParseResultOutcome(tx *transaction.ApiTransactionResult, pubKeyConverter co return nil, fmt.Errorf("failed to parse outcome on write log where first topic equals address: %w", err) } if outcome != nil { - log.Trace("on writelog with topics[0] == tx.sender") + log.Trace("txHash [%s] result outcome on write log where first topic equals address", tx.Hash) return outcome, nil } - outcome, err = parseOutcomeWithFallbackHeuristics(tx, *metadata) + outcome, err = parseOutcomeWithFallbackHeuristics(tx, tx.Receivers[0]) if outcome != nil { - log.Trace("result outcome on fallback heuristics") - panic("whatever") + log.Trace("txHash [%s] result outcome on fallback heuristics", tx.Hash) return outcome, nil } return nil, nil @@ -114,7 +100,6 @@ func parseOutcomeOnSimpleMoveBalance(tx *transaction.ApiTransactionResult) *Resu func parseOutcomeOnInvalidTransaction(tx *transaction.ApiTransactionResult) *ResultOutcome { if tx.Status == transaction.TxStatusInvalid { - if tx.Receipt != nil && tx.Receipt.Data != "" { return &ResultOutcome{ ReturnCode: OutOfFunds, @@ -128,27 +113,27 @@ func parseOutcomeOnInvalidTransaction(tx *transaction.ApiTransactionResult) *Res return nil } -func parseOutcomeOnEasilyFoundResultWithReturnData(results []*transaction.ApiSmartContractResult) (*ResultOutcome, error) { - var r *transaction.ApiSmartContractResult - for _, result := range results { - if result.Nonce != 0 && result.Data != "" && result.Data[0] == '@' { - r = result +func parseOutcomeOnEasilyFoundResultWithReturnData(scResults []*transaction.ApiSmartContractResult) (*ResultOutcome, error) { + var scr *transaction.ApiSmartContractResult + for _, scResult := range scResults { + if scResult.Nonce != 0 && scResult.Data != "" && scResult.Data[0] == '@' { + scr = scResult break } } - if r == nil { + if scr == nil { return nil, nil } - returnCode, returnDataParts, err := sliceDataFieldInParts(r.Data) + returnCode, returnDataParts, err := sliceDataFieldInParts(scr.Data) if err != nil { return nil, fmt.Errorf("failed to slice data field in parts: %w", err) } returnMessage := returnCode.String() - if r.ReturnMessage != "" { - returnMessage = r.ReturnMessage + if scr.ReturnMessage != "" { + returnMessage = scr.ReturnMessage } return &ResultOutcome{ @@ -269,7 +254,7 @@ func parseOutcomeOnWriteLogWhereFirstTopicEqualsAddress(logs *transaction.ApiLog }, nil } -func parseOutcomeWithFallbackHeuristics(tx *transaction.ApiTransactionResult, metadata transactionDecoder.TransactionMetadata) (*ResultOutcome, error) { +func parseOutcomeWithFallbackHeuristics(tx *transaction.ApiTransactionResult, receiver string) (*ResultOutcome, error) { for _, resultItem := range tx.SmartContractResults { event, findErr := findSingleOrNoneEvent(resultItem.Logs, OnWriteLog, func(e *transaction.Events) *transaction.Events { addressIsSender := e.Address == tx.Sender @@ -279,7 +264,7 @@ func parseOutcomeWithFallbackHeuristics(tx *transaction.ApiTransactionResult, me if topicDecode != nil { return nil } - firstTopicIsContract = string(decodeString) == metadata.Receiver + firstTopicIsContract = string(decodeString) == receiver if firstTopicIsContract && addressIsSender { return e @@ -312,6 +297,10 @@ func parseOutcomeWithFallbackHeuristics(tx *transaction.ApiTransactionResult, me } func sliceDataFieldInParts(data string) (*ReturnCode, []*bytes.Buffer, error) { + if data == "" { + return nil, nil, ErrEmptyDataField + } + // By default, skip the first part, which is usually empty (e.g. "[empty]@6f6b") startingIndex := 1 @@ -325,11 +314,15 @@ func sliceDataFieldInParts(data string) (*ReturnCode, []*bytes.Buffer, error) { // TODO: make this a function that returns a slice of bytes parts := stringToBuffers(data) + if len(parts) <= startingIndex { + return nil, nil, ErrCannotProcessDataField + } + returnCodePart := parts[startingIndex] returnDataParts := parts[startingIndex+1:] if returnCodePart.Len() == 0 { - return nil, nil, errors.New("no return code") + return nil, nil, ErrNoReturnCode } returnCode := fromBuffer(*returnCodePart) @@ -350,16 +343,16 @@ func stringToBuffers(joinedString string) []*bytes.Buffer { func findSingleOrNoneEvent( logs *transaction.ApiLogs, identifier string, - predicate func(e *transaction.Events) *transaction.Events, + filter func(e *transaction.Events) *transaction.Events, ) (*transaction.Events, error) { if logs == nil { return nil, nil } - events := findEvents(logs.Events, identifier, predicate) + events := findEvents(logs.Events, identifier, filter) if len(events) > 1 { - return nil, errors.New("found more than one event") + return nil, ErrFoundMoreThanOneEvent } if events == nil { @@ -369,12 +362,12 @@ func findSingleOrNoneEvent( return events[0], nil } -func findEvents(events []*transaction.Events, identifier string, predicate func(e *transaction.Events) *transaction.Events) []*transaction.Events { +func findEvents(events []*transaction.Events, identifier string, filter func(e *transaction.Events) *transaction.Events) []*transaction.Events { var matches []*transaction.Events for _, event := range events { if event.Identifier == identifier { - if predicate != nil { - e := predicate(event) + if filter != nil { + e := filter(event) if e != nil { matches = append(matches, e) @@ -388,9 +381,9 @@ func findEvents(events []*transaction.Events, identifier string, predicate func( return matches } -func findFirstOrNoneTopic(topics [][]byte, predicate func(topic []byte) []byte) []byte { +func findFirstOrNoneTopic(topics [][]byte, filter func(topic []byte) []byte) []byte { for _, topic := range topics { - t := predicate(topic) + t := filter(topic) if t != nil { return t } diff --git a/process/resultsParser/resultsParser_test.go b/process/resultsParser/resultsParser_test.go index 8f0350a7..080ed742 100644 --- a/process/resultsParser/resultsParser_test.go +++ b/process/resultsParser/resultsParser_test.go @@ -138,6 +138,77 @@ func TestResultsParser_RealWorld(t *testing.T) { } } +func Test_SliceDataInFields(t *testing.T) { + t.Parallel() + + t.Run("empty data in fields", func(t *testing.T) { + t.Parallel() + + data := "" + returnCode, bufferBytes, err := sliceDataFieldInParts(data) + + require.Equal(t, ErrEmptyDataField, err) + require.Nil(t, returnCode) + require.Nil(t, bufferBytes) + }) + + t.Run("incomprehensible data field", func(t *testing.T) { + t.Parallel() + + data := "claimRewards" + returnCode, bufferBytes, err := sliceDataFieldInParts(data) + + require.Equal(t, ErrCannotProcessDataField, err) + require.Nil(t, returnCode) + require.Nil(t, bufferBytes) + }) + + t.Run("esdt transfer with arguments", func(t *testing.T) { + t.Parallel() + + data := "ESDTTransfer@4245452d636233376236@05f98a44@73776170546f6b656e734669786564496e707574@5745474c442d626434643739@b87ebb42bad228" + rc := fromBuffer(*bytes.NewBufferString("73776170546f6b656e734669786564496e707574")) + + returnCode, bufferBytes, err := sliceDataFieldInParts(data) + require.NoError(t, err) + require.Equal(t, &rc, returnCode) + require.Len(t, bufferBytes, 2) + }) + + t.Run("esdt transfer with less arguments", func(t *testing.T) { + t.Parallel() + + data := "ESDTTransfer@4245452d636233376236@05f98a44" + rc := fromBuffer(*bytes.NewBufferString("73776170546f6b656e734669786564496e707574")) + + returnCode, bufferBytes, err := sliceDataFieldInParts(data) + require.NoError(t, err) + require.Equal(t, &rc, returnCode) + require.Len(t, bufferBytes, 2) + }) + + t.Run("esdt transfer with no arguments", func(t *testing.T) { + t.Parallel() + + data := "ESDTTransfer" + returnCode, bufferBytes, err := sliceDataFieldInParts(data) + require.Equal(t, ErrCannotProcessDataField, err) + require.Nil(t, returnCode) + require.Nil(t, bufferBytes) + }) + + t.Run("data field unknown format", func(t *testing.T) { + t.Parallel() + + data := "aaa@@" + returnCode, bufferBytes, err := sliceDataFieldInParts(data) + require.Equal(t, ErrNoReturnCode, err) + require.Nil(t, returnCode) + require.Nil(t, bufferBytes) + }) + +} + func readJSONFromFile(filePath string) ([]*transaction.ApiTransactionResult, error) { file, err := os.Open(filePath) if err != nil { diff --git a/process/resultsParser/returnCode.go b/process/resultsParser/returnCode.go index 5ec72059..abfc24ea 100644 --- a/process/resultsParser/returnCode.go +++ b/process/resultsParser/returnCode.go @@ -9,19 +9,10 @@ import ( type ReturnCode string const ( - None ReturnCode = "" - Ok ReturnCode = "ok" - FunctionNotFound ReturnCode = "function not found" - FunctionWrongSignature ReturnCode = "wrong signature for function" - ContractNotFound ReturnCode = "contract not found" - UserError ReturnCode = "user error" - OutOfGas ReturnCode = "out of gas" - AccountCollision ReturnCode = "account collision" - OutOfFunds ReturnCode = "out of funds" - CallStackOverFlow ReturnCode = "call stack overflow" - ContractInvalid ReturnCode = "contract invalid" - ExecutionFailed ReturnCode = "execution failed" - Unknown ReturnCode = "unknown" + None ReturnCode = "" + Ok ReturnCode = "ok" + UserError ReturnCode = "user error" + OutOfFunds ReturnCode = "out of funds" ) func (rc ReturnCode) String() string { diff --git a/process/resultsParser/transactionDecoder/errors.go b/process/resultsParser/transactionDecoder/errors.go deleted file mode 100644 index a5b0f638..00000000 --- a/process/resultsParser/transactionDecoder/errors.go +++ /dev/null @@ -1,28 +0,0 @@ -package transactionDecoder - -import ( - "errors" -) - -var ( - // ErrValueSet will signal a parsing error from string to big.Int - ErrValueSet = errors.New("failed to set Value") - - // ErrNotESDTTransfer will signal that the transaction is not an ESDTTransfer. - ErrNotESDTTransfer = errors.New("not ESDTTransfer transaction metadata") - - // ErrNotESDTNFTTransfer will signal that the transaction is not an ESDTNFTTransfer. - ErrNotESDTNFTTransfer = errors.New("not ESDTNFTTransfer transaction metadata") - - // ErrNotMultiESDTNFTTransfer will signal that the transaction is not a MultiESDTNFTTransfer. - ErrNotMultiESDTNFTTransfer = errors.New("not MultiESDTNFTTransfer transaction metadata") - - // ErrNoArgs will signal that the transaction does not have any function arguments. - ErrNoArgs = errors.New("no arguments provided") - - // ErrSenderReceiver will signal that the sender and receiver address in the transaction do not match. - ErrSenderReceiver = errors.New("sender does not match receiver") - - // ErrInvalidAddress will signal that the address in invalid. - ErrInvalidAddress = errors.New("invalid address") -) diff --git a/process/resultsParser/transactionDecoder/metadata.go b/process/resultsParser/transactionDecoder/metadata.go deleted file mode 100644 index 4cb35652..00000000 --- a/process/resultsParser/transactionDecoder/metadata.go +++ /dev/null @@ -1,40 +0,0 @@ -package transactionDecoder - -import ( - "math/big" -) - -type ( - // TransactionToDecode is the transaction whose Data field will be later decoded into metadata. - TransactionToDecode struct { - Sender string `json:"sender"` - Receiver string `json:"receiver"` - Data []byte `json:"data"` - Value string `json:"value"` - } - - // TransactionMetadata is the result of the decoded data field. - TransactionMetadata struct { - Sender string - Receiver string - Value *big.Int - - FunctionName string - FunctionArgs []string - - Transfers []TransactionMetadataTransfer - } - - // TransactionMetadataTransfer contains information about the transfers within the transaction. - TransactionMetadataTransfer struct { - Properties TokenTransferProperties - Value *big.Int - } - - // TokenTransferProperties holds information about the token that has been transferred in the transaction. - TokenTransferProperties struct { - Token string - Collection string - Identifier string - } -) diff --git a/process/resultsParser/transactionDecoder/transactionDecoder.go b/process/resultsParser/transactionDecoder/transactionDecoder.go deleted file mode 100644 index 2f3a9e5d..00000000 --- a/process/resultsParser/transactionDecoder/transactionDecoder.go +++ /dev/null @@ -1,413 +0,0 @@ -package transactionDecoder - -import ( - "encoding/base64" - "encoding/hex" - "encoding/json" - "fmt" - "math/big" - "strconv" - "strings" - - "github.com/multiversx/mx-chain-core-go/core" -) - -// GetTransactionMetadata will decode the Data field from the transaction and populate it in the TransactionMetadata. -func GetTransactionMetadata(tx *TransactionToDecode, pubKeyConverter core.PubkeyConverter) (*TransactionMetadata, error) { - metadata, err := getNormalTransactionMetadata(tx, pubKeyConverter) - if err != nil { - return nil, fmt.Errorf("failed to retrieve transaction metadata: %w", err) - } - - esdtMetadata, err := getESDTTransactionMetadata(*metadata) - if err == nil { - return esdtMetadata, nil - } - - nftMetadata, err := getNFTTransferMetadata(*metadata, pubKeyConverter) - if err == nil { - return nftMetadata, nil - } - - multiMetadata, err := getMultiTransferMetadata(*metadata, pubKeyConverter) - if err == nil { - return multiMetadata, nil - } - - return metadata, nil -} - -func getNormalTransactionMetadata(tx *TransactionToDecode, pubKeyConverter core.PubkeyConverter) (*TransactionMetadata, error) { - v := "0" - if tx.Value != "" { - v = tx.Value - } - value, ok := big.NewInt(0).SetString(v, 10) - if !ok { - return nil, ErrValueSet - } - - metadata := TransactionMetadata{ - Sender: tx.Sender, - Receiver: tx.Receiver, - Value: value, - } - - data := string(tx.Data) - if data != "" { - dataComponents := strings.Split(data, "@") - - everyCheck := true - args := dataComponents[1:] - for _, arg := range args { - if !isSmartContractArgument(arg) { - everyCheck = false - } - } - - if everyCheck { - metadata.FunctionName = dataComponents[0] - metadata.FunctionArgs = args - } - - if metadata.FunctionName == "relayedTx" && metadata.FunctionArgs != nil && len(metadata.FunctionArgs) == 1 { - relayedTx, relayErr := parseRelayedV1(metadata, pubKeyConverter) - if relayErr != nil { - return nil, fmt.Errorf("failed to parse relayed v1 transaction metadata: %w", relayErr) - } - - return getNormalTransactionMetadata(relayedTx, pubKeyConverter) - } - - if metadata.FunctionName == "relayedTxV2" && - metadata.FunctionArgs != nil && - len(metadata.FunctionArgs) == 4 { - - relayedTxV2, relayErr := parseRelayedV2(metadata, pubKeyConverter) - if relayErr != nil { - return nil, fmt.Errorf("failed to parse relayed v2 transaction metadata: %w", relayErr) - } - - return getNormalTransactionMetadata(relayedTxV2, pubKeyConverter) - } - - } - - return &metadata, nil - -} - -func parseRelayedV1(metadata TransactionMetadata, pubKeyConverter core.PubkeyConverter) (*TransactionToDecode, error) { - data, err := hex.DecodeString(metadata.FunctionArgs[0]) - if err != nil { - return nil, fmt.Errorf("failed to decode data field: %w", err) - } - - var innerTx = struct { - Nonce int `json:"nonce"` - Sender string `json:"sender"` - Receiver string `json:"receiver"` - Value int `json:"value"` - GasPrice int `json:"gasPrice"` - GasLimit int `json:"gasLimit"` - Data []byte `json:"data"` - Signature string `json:"signature"` - ChainID string `json:"chainID"` - Version int `json:"version"` - }{} - err = json.Unmarshal(data, &innerTx) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal inner transaction: %w", err) - } - sender, err := retrieveAddressBech32FromBase64(innerTx.Sender, pubKeyConverter) - if err != nil { - return nil, fmt.Errorf("failed to decode sender field: %w", err) - } - receiver, err := retrieveAddressBech32FromBase64(innerTx.Receiver, pubKeyConverter) - if err != nil { - return nil, fmt.Errorf("failed to decode receiver field: %w", err) - } - - relayedTx := &TransactionToDecode{ - Sender: sender, - Receiver: receiver, - Data: innerTx.Data, - Value: strconv.FormatInt(int64(innerTx.Value), 10), - } - return relayedTx, nil -} - -func parseRelayedV2(metadata TransactionMetadata, pubKeyConverter core.PubkeyConverter) (*TransactionToDecode, error) { - relayedTx := &TransactionToDecode{} - - relayedTx.Sender = metadata.Receiver - receiver, err := retrieveAddressBech32FromHex(metadata.FunctionArgs[0], pubKeyConverter) - if err != nil { - return nil, fmt.Errorf("failed to decode receiver field: %w", err) - } - relayedTx.Receiver = receiver - - decodeString, err := hex.DecodeString(metadata.FunctionArgs[2]) - if err != nil { - return nil, fmt.Errorf("failed to decode data field: %w", err) - } - relayedTx.Data = decodeString - relayedTx.Value = "0" - - return relayedTx, nil -} - -func getESDTTransactionMetadata(metadata TransactionMetadata) (*TransactionMetadata, error) { - if metadata.FunctionName != "ESDTTransfer" { - return nil, ErrNotESDTTransfer - } - - args := metadata.FunctionArgs - if args == nil { - return nil, ErrNoArgs - } - - if len(args) < 2 { - return nil, fmt.Errorf("not enough arguments. required [%d], found [%d]", 2, len(args)) - } - - tokenIdentifier, err := hex.DecodeString(args[0]) - if err != nil { - return nil, fmt.Errorf("failed to decode token identifier: %w", err) - } - - value, ok := big.NewInt(0).SetString(args[1], 16) - if !ok { - return nil, fmt.Errorf("failed to set value: %w", err) - } - - result := &TransactionMetadata{} - result.Sender = metadata.Sender - result.Receiver = metadata.Receiver - result.Value = value - - if len(args) > 2 { - functionName, err := hex.DecodeString(args[2]) - if err != nil { - return nil, fmt.Errorf("failed to decode function name: %w", err) - } - result.FunctionName = string(functionName) - result.FunctionArgs = args[3:] - } - - result.Transfers = []TransactionMetadataTransfer{ - { - Value: value, - Properties: TokenTransferProperties{ - Identifier: string(tokenIdentifier), - }, - }, - } - - return result, nil -} - -func getNFTTransferMetadata(metadata TransactionMetadata, pubKeyConverter core.PubkeyConverter) (*TransactionMetadata, error) { - if metadata.Sender != metadata.Receiver { - return nil, ErrSenderReceiver - } - - if metadata.FunctionName != "ESDTNFTTransfer" { - return nil, ErrNotESDTNFTTransfer - } - - args := metadata.FunctionArgs - if args == nil { - return nil, ErrNoArgs - } - - if len(args) < 4 { - return nil, fmt.Errorf("not enough arguments. required [%d], found [%d]", 2, len(args)) - } - - if !isAddressValid(args[3]) { - return nil, ErrInvalidAddress - } - - collectionIdentifier, err := hex.DecodeString(args[0]) - if err != nil { - return nil, fmt.Errorf("failed to decode collection identifier: %w", err) - } - - nonce := args[1] - value, ok := big.NewInt(0).SetString(args[2], 16) - if !ok { - return nil, ErrValueSet - } - - receiver, err := retrieveAddressBech32FromHex(args[3], pubKeyConverter) - if err != nil { - return nil, fmt.Errorf("failed to decode receiver field: %w", err) - } - - result := &TransactionMetadata{ - Sender: metadata.Sender, - Receiver: receiver, - Value: value, - } - - if len(args) > 4 { - functionName, err := hex.DecodeString(args[4]) - if err != nil { - return nil, fmt.Errorf("failed to decode function name: %w", err) - } - result.FunctionName = string(functionName) - result.FunctionArgs = args[5:] - } - - result.Transfers = []TransactionMetadataTransfer{ - { - Value: value, - Properties: TokenTransferProperties{ - Collection: string(collectionIdentifier), - Identifier: fmt.Sprintf("%s-%s", collectionIdentifier, nonce), - }, - }, - } - - return result, nil -} - -func getMultiTransferMetadata(metadata TransactionMetadata, pubKeyConverter core.PubkeyConverter) (*TransactionMetadata, error) { - if metadata.Sender != metadata.Receiver { - return nil, ErrSenderReceiver - } - - if metadata.FunctionName != "MultiESDTNFTTransfer" { - return nil, ErrNotMultiESDTNFTTransfer - } - - if metadata.FunctionArgs == nil { - return nil, ErrNoArgs - } - - if len(metadata.FunctionArgs) < 3 { - return nil, fmt.Errorf("not enough arguments. required [%d], found [%d]", 3, len(metadata.FunctionArgs)) - } - - args := metadata.FunctionArgs - if !isAddressValid(args[0]) { - return nil, ErrInvalidAddress - } - - receiver, err := retrieveAddressBech32FromHex(args[0], pubKeyConverter) - if err != nil { - return nil, fmt.Errorf("failed to decode receiver field: %w", err) - } - - transferCount, err := strconv.ParseInt(args[1], 16, 64) - if err != nil { - return nil, fmt.Errorf("failed to parse transfer count: %w", err) - } - - result := &TransactionMetadata{} - transfers := make([]TransactionMetadataTransfer, 0) - index := 2 - for i := int64(0); i < transferCount; i++ { - identifier, err := hex.DecodeString(args[index]) - if err != nil { - return nil, fmt.Errorf("failed to decode transfer identifier: %w", err) - } - index++ - - n, nonceErr := strconv.ParseInt(args[index], 16, 64) - nonce := args[index] - index++ - - value, ok := big.NewInt(0).SetString(args[index], 16) - if !ok { - return nil, fmt.Errorf("failed to set value: %w", err) - } - index++ - if nonceErr == nil && n > 0 { - transfers = append(transfers, TransactionMetadataTransfer{ - Value: value, - Properties: TokenTransferProperties{ - Collection: string(identifier), - Identifier: fmt.Sprintf("%s-%s", identifier, nonce), - }, - }) - } else { - transfers = append(transfers, TransactionMetadataTransfer{ - Value: value, - Properties: TokenTransferProperties{ - Token: string(identifier), - }, - }) - } - } - - result.Sender = metadata.Sender - result.Receiver = receiver - result.Transfers = transfers - result.Value = big.NewInt(0) - - if len(args) > index { - functionName, err := hex.DecodeString(args[index]) - if err != nil { - return nil, fmt.Errorf("failed to decode function name: %w", err) - } - result.FunctionName = string(functionName) - index++ - result.FunctionArgs = args[index:] - } - - return result, nil -} - -func isSmartContractArgument(arg string) bool { - if _, err := hex.DecodeString(arg); err != nil { - return false - } - - if len(arg)%2 != 0 { - return false - } - - return true -} - -func isAddressValid(address string) bool { - decodeString, err := hex.DecodeString(address) - if err != nil { - return false - } - - if len(decodeString) != 32 { - return false - } - - return true -} - -func retrieveAddressBech32FromBase64(base64Encoded string, pubKeyConverter core.PubkeyConverter) (string, error) { - hexEncoded, err := base64.StdEncoding.DecodeString(base64Encoded) - if err != nil { - return "", err - } - - encode, err := pubKeyConverter.Encode(hexEncoded) - if err != nil { - return "", err - } - - return encode, nil -} - -func retrieveAddressBech32FromHex(hexEncoded string, pubKeyConverter core.PubkeyConverter) (string, error) { - bytes, err := hex.DecodeString(hexEncoded) - if err != nil { - return "", err - } - address, err := pubKeyConverter.Encode(bytes) - if err != nil { - return "", err - } - - return address, nil -} diff --git a/process/resultsParser/transactionDecoder/transactionDecoder_test.go b/process/resultsParser/transactionDecoder/transactionDecoder_test.go deleted file mode 100644 index 87d46fe5..00000000 --- a/process/resultsParser/transactionDecoder/transactionDecoder_test.go +++ /dev/null @@ -1,199 +0,0 @@ -package transactionDecoder - -import ( - "encoding/json" - "fmt" - "math/big" - "testing" - - "github.com/multiversx/mx-chain-core-go/core/pubkeyConverter" - "github.com/stretchr/testify/require" -) - -var testPubkeyConverter, _ = pubkeyConverter.NewBech32PubkeyConverter(32, "erd") - -func Test_GetTransactionMetadata(t *testing.T) { - t.Parallel() - - t.Run("NFT Smart contract call", func(t *testing.T) { - t.Parallel() - - jsonTx := `{ - "sender" : "erd18w6yj09l9jwlpj5cjqq9eccfgulkympv7d4rj6vq4u49j8fpwzwsvx7e85", - "receiver" : "erd18w6yj09l9jwlpj5cjqq9eccfgulkympv7d4rj6vq4u49j8fpwzwsvx7e85", - "data" : "RVNEVE5GVFRyYW5zZmVyQDRjNGI0ZDQ1NTgyZDYxNjE2MjM5MzEzMEAyZmI0ZTlAZTQwZjE2OTk3MTY1NWU2YmIwNGNAMDAwMDAwMDAwMDAwMDAwMDA1MDBkZjNiZWJlMWFmYTEwYzQwOTI1ZTgzM2MxNGE0NjBlMTBhODQ5ZjUwYTQ2OEA3Mzc3NjE3MDVmNmM2YjZkNjU3ODVmNzQ2ZjVmNjU2NzZjNjRAMGIzNzdmMjYxYzNjNzE5MUA=", - "value" : "0" -}` - var tx TransactionToDecode - err := json.Unmarshal([]byte(jsonTx), &tx) - require.NoError(t, err) - - metadata, err := GetTransactionMetadata(&tx, testPubkeyConverter) - require.NoError(t, err) - require.Equal(t, tx.Sender, metadata.Sender) - require.Equal(t, "erd1qqqqqqqqqqqqqpgqmua7hcd05yxypyj7sv7pffrquy9gf86s535qxct34s", metadata.Receiver) - value, _ := big.NewInt(0).SetString("1076977887712805212893260", 10) - require.Equal(t, value, metadata.Value) - require.Equal(t, "swap_lkmex_to_egld", metadata.FunctionName) - require.Equal(t, []string{"0b377f261c3c7191", ""}, metadata.FunctionArgs) - value1, _ := big.NewInt(0).SetString("1076977887712805212893260", 10) - require.Equal(t, []TransactionMetadataTransfer{ - { - Value: value1, - Properties: TokenTransferProperties{ - Identifier: "LKMEX-aab910-2fb4e9", - Collection: "LKMEX-aab910", - }, - }, - }, metadata.Transfers) - - fmt.Println(metadata) - }) - - t.Run("ESDT Transfer", func(t *testing.T) { - t.Parallel() - - jsonTx := `{ - "sender" : "erd1jvc6nyyl73q2yardw7dj8235h5zqaum4qyc8wlgs6aa26seysuvsrp48x2", - "receiver" : "erd1flqg2zf3knya94lcupscdwmrud029mes8a85r202rvwpzjyk5tjqxt8dxu", - "data" : "RVNEVFRyYW5zZmVyQDUwNGM0MTU0NDEyZDM5NjI2MTM2NjMzM0AwMTJhMDVmMjAw", - "value" : "0" -}` - var tx TransactionToDecode - err := json.Unmarshal([]byte(jsonTx), &tx) - require.NoError(t, err) - - metadata, err := GetTransactionMetadata(&tx, testPubkeyConverter) - require.NoError(t, err) - require.Equal(t, tx.Sender, metadata.Sender) - require.Equal(t, tx.Receiver, metadata.Receiver) - value, _ := big.NewInt(0).SetString("5000000000", 10) - require.Equal(t, value, metadata.Value) - require.Equal(t, []TransactionMetadataTransfer{{Value: value, Properties: TokenTransferProperties{Identifier: "PLATA-9ba6c3"}}}, metadata.Transfers) - }) - - t.Run("MultiESDTNFTTransfer fungible (with 00 nonce) + meta", func(t *testing.T) { - t.Parallel() - - jsonTx := `{ - "sender" : "erd1lkrrrn3ws9sp854kdpzer9f77eglqpeet3e3k3uxvqxw9p3eq6xqxj43r9", - "receiver" : "erd1lkrrrn3ws9sp854kdpzer9f77eglqpeet3e3k3uxvqxw9p3eq6xqxj43r9", - "data" : "TXVsdGlFU0RUTkZUVHJhbnNmZXJAMDAwMDAwMDAwMDAwMDAwMDA1MDBkZjNiZWJlMWFmYTEwYzQwOTI1ZTgzM2MxNGE0NjBlMTBhODQ5ZjUwYTQ2OEAwMkA0YzRiNGQ0NTU4MmQ2MTYxNjIzOTMxMzBAMmZlM2IwQDA5Yjk5YTZkYjMwMDI3ZTRmM2VjQDU1NTM0NDQzMmQzMzM1MzA2MzM0NjVAMDBAMDEyNjMwZTlhMjlmMmY5MzgxNDQ5MUA3MDYxNzk1ZjZkNjU3NDYxNWY2MTZlNjQ1ZjY2NzU2ZTY3Njk2MjZjNjVAMGVkZTY0MzExYjhkMDFiNUA=", - "value" : "0" -}` - var tx TransactionToDecode - err := json.Unmarshal([]byte(jsonTx), &tx) - require.NoError(t, err) - - metadata, err := GetTransactionMetadata(&tx, testPubkeyConverter) - require.NoError(t, err) - - require.NoError(t, err) - require.Equal(t, tx.Sender, metadata.Sender) - require.Equal(t, "erd1qqqqqqqqqqqqqpgqmua7hcd05yxypyj7sv7pffrquy9gf86s535qxct34s", metadata.Receiver) - require.Equal(t, tx.Value, metadata.Value.String()) - require.Equal(t, "pay_meta_and_fungible", metadata.FunctionName) - require.Equal(t, []string{"0ede64311b8d01b5", ""}, metadata.FunctionArgs) - value1, _ := big.NewInt(0).SetString("45925073746530627023852", 10) - value2, _ := big.NewInt(0).SetString("1389278024872597502641297", 10) - require.Equal(t, []TransactionMetadataTransfer{ - { - Value: value1, - Properties: TokenTransferProperties{ - Identifier: "LKMEX-aab910-2fe3b0", - Collection: "LKMEX-aab910", - }, - }, - { - Value: value2, - Properties: TokenTransferProperties{ - Token: "USDC-350c4e", - }, - }, - }, metadata.Transfers) - }) - - t.Run("MultiESDTNFTTransfer fungibles (00 and missing nonce)", func(t *testing.T) { - t.Parallel() - - jsonTx := `{ - "sender" : "erd1lkrrrn3ws9sp854kdpzer9f77eglqpeet3e3k3uxvqxw9p3eq6xqxj43r9", - "receiver" : "erd1lkrrrn3ws9sp854kdpzer9f77eglqpeet3e3k3uxvqxw9p3eq6xqxj43r9", - "data" : "TXVsdGlFU0RUTkZUVHJhbnNmZXJAMDAwMDAwMDAwMDAwMDAwMDA1MDBkZjNiZWJlMWFmYTEwYzQwOTI1ZTgzM2MxNGE0NjBlMTBhODQ5ZjUwYTQ2OEAwMkA1MjQ5NDQ0NTJkMzAzNTYyMzE2MjYyQDAwQDA5Yjk5YTZkYjMwMDI3ZTRmM2VjQDU1NTM0NDQzMmQzMzM1MzA2MzM0NjVAQDAxMjYzMGU5YTI5ZjJmOTM4MTQ0OTE=", - "value" : "0" -}` - var tx TransactionToDecode - err := json.Unmarshal([]byte(jsonTx), &tx) - require.NoError(t, err) - - metadata, err := GetTransactionMetadata(&tx, testPubkeyConverter) - require.NoError(t, err) - require.Equal(t, tx.Sender, metadata.Sender) - require.Equal(t, "erd1qqqqqqqqqqqqqpgqmua7hcd05yxypyj7sv7pffrquy9gf86s535qxct34s", metadata.Receiver) - require.Equal(t, tx.Value, metadata.Value.String()) - value1, _ := big.NewInt(0).SetString("45925073746530627023852", 10) - value2, _ := big.NewInt(0).SetString("1389278024872597502641297", 10) - require.Equal(t, []TransactionMetadataTransfer{ - { - Value: value1, - Properties: TokenTransferProperties{ - Token: "RIDE-05b1bb", - }, - }, - { - Value: value2, - Properties: TokenTransferProperties{ - Token: "USDC-350c4e", - }, - }, - }, metadata.Transfers) - }) - - t.Run("Relayed transaction v1", func(t *testing.T) { - t.Parallel() - - jsonTx := `{ - "sender" : "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", - "receiver" : "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", - "data" : "cmVsYXllZFR4QDdiMjI2ZTZmNmU2MzY1MjIzYTMxMzkzODJjMjI3MzY1NmU2NDY1NzIyMjNhMjI2NzQ1NmU1NzRmNjU1NzZkNmQ0MTMwNjMzMDZhNmI3MTc2NGQzNTQyNDE3MDdhNjE2NDRiNDY1NzRlNTM0ZjY5NDE3NjQzNTc1MTYzNzc2ZDQ3NTA2NzNkMjIyYzIyNzI2NTYzNjU2OTc2NjU3MjIyM2EyMjQxNDE0MTQxNDE0MTQxNDE0MTQxNDE0NjQxNDIzNDc1NTk1MjcxNjMzNDY1NDQ0OTM0Nzk2NzM4N2E0ODc3NjI0NDMwNWE2ODZiNTg0MjM1NzAzMTc3M2QyMjJjMjI3NjYxNmM3NTY1MjIzYTMwMmMyMjY3NjE3MzUwNzI2OTYzNjUyMjNhMzEzMDMwMzAzMDMwMzAzMDMwMzAyYzIyNjc2MTczNGM2OTZkNjk3NDIyM2EzNjMwMzAzMDMwMzAzMDMwMmMyMjY0NjE3NDYxMjIzYTIyNTk1NzUyNmIyMjJjMjI3MzY5Njc2ZTYxNzQ3NTcyNjUyMjNhMjI0ZTMwNzIzMTcwNmYzNzZiNzY0ZjU0NGI0OTQ3NDcyZjc1NmI2NzcyMzg1YTYyNTc2NDU4NjczMTY2NTEzMDc2NmQ3NTYyMzU3OTM0NGY3MzUzNDE3MTM0N2EyZjU5Mzc2YzQ2NTI3OTU3NzM2NzM0NGUyYjZmNGE2OTQ5NDk1Nzc3N2E2YjZkNmM2YTQ5NDE3MjZkNjkzMTY5NTg0ODU0NzkzNDRiNjc0MTQxM2QzZDIyMmMyMjYzNjg2MTY5NmU0OTQ0MjIzYTIyNTY0MTNkM2QyMjJjMjI3NjY1NzI3MzY5NmY2ZTIyM2EzMTdk", - "value" : "0" -}` - var tx TransactionToDecode - err := json.Unmarshal([]byte(jsonTx), &tx) - require.NoError(t, err) - - metadata, err := GetTransactionMetadata(&tx, testPubkeyConverter) - require.NoError(t, err) - - require.NoError(t, err) - require.Equal(t, "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", metadata.Sender) - require.Equal(t, "erd1qqqqqqqqqqqqqpgqrchxzx5uu8sv3ceg8nx8cxc0gesezure5awqn46gtd", metadata.Receiver) - require.Equal(t, tx.Value, metadata.Value.String()) - require.Equal(t, "add", metadata.FunctionName) - require.Empty(t, metadata.FunctionArgs) - }) - - t.Run("Relayed transaction v2", func(t *testing.T) { - t.Parallel() - - jsonTx := `{ - "sender" : "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", - "receiver" : "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", - "data" : "cmVsYXllZFR4VjJAMDAwMDAwMDAwMDAwMDAwMDA1MDAxZTJlNjExYTljZTFlMGM4ZTMyODNjY2M3YzFiMGY0NjYxOTE3MDc5YTc1Y0AwZkA2MTY0NjRAOWFiZDEzZjRmNTNmM2YyMzU5Nzc0NGQ2NWZjNWQzNTFiYjY3NzNlMDVhOTU0YjQxOWMwOGQxODU5M2QxYzY5MjYyNzlhNGQxNjE0NGQzZjg2NmE1NDg3ODAzMTQyZmNmZjBlYWI2YWQ1ODgyMDk5NjlhY2I3YWJlZDIxMDIwMGI=", - "value" : "0" -}` - var tx TransactionToDecode - err := json.Unmarshal([]byte(jsonTx), &tx) - require.NoError(t, err) - - metadata, err := GetTransactionMetadata(&tx, testPubkeyConverter) - require.NoError(t, err) - - require.NoError(t, err) - require.Equal(t, "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", metadata.Sender) - require.Equal(t, "erd1qqqqqqqqqqqqqpgqrchxzx5uu8sv3ceg8nx8cxc0gesezure5awqn46gtd", metadata.Receiver) - require.Equal(t, tx.Value, metadata.Value.String()) - require.Equal(t, "add", metadata.FunctionName) - require.Empty(t, metadata.FunctionArgs) - }) -}