From 247af0200c14cab4aa4ec255127e4bad21b44d9c Mon Sep 17 00:00:00 2001
From: Artyom Sayadyan <xrtm000@gmail.com>
Date: Mon, 15 Jan 2024 15:15:37 +0300
Subject: [PATCH] NODE-2643 Fixed EthereumInvokeScript serialization (#3932)

Co-authored-by: Sergey Nazarov <snazarov@uniqsoft.ae>
---
 .../api/http/TransactionJsonSerializer.scala  |  7 ++-
 .../http/TransactionsRouteSpec.scala          | 62 ++++++++++++++++++-
 2 files changed, 65 insertions(+), 4 deletions(-)

diff --git a/node/src/main/scala/com/wavesplatform/api/http/TransactionJsonSerializer.scala b/node/src/main/scala/com/wavesplatform/api/http/TransactionJsonSerializer.scala
index 5588765144..11d8287c09 100644
--- a/node/src/main/scala/com/wavesplatform/api/http/TransactionJsonSerializer.scala
+++ b/node/src/main/scala/com/wavesplatform/api/http/TransactionJsonSerializer.scala
@@ -285,9 +285,10 @@ final case class TransactionJsonSerializer(blockchain: Blockchain) {
           val payments       = i.payments.map(p => InvokeScriptTransaction.Payment(p.amount, PBAmounts.toVanillaAssetId(p.assetId)))
 
           gen.writeStartObject()
+          gen.writeNumberField("type", tx.tpe.id, numbersAsString)
           gen.writeStringField("id", tx.id().toString)
           gen.writeNumberField("fee", tx.assetFee._2, numbersAsString)
-          tx.assetFee._1.maybeBase58Repr.foreach(gen.writeStringField("feeAssetId", _))
+          gen.writeStringField("feeAssetId", null)
           gen.writeNumberField("timestamp", tx.timestamp, numbersAsString)
           gen.writeNumberField("version", 1, numbersAsString)
           gen.writeNumberField("chainId", tx.chainId, numbersAsString)
@@ -302,6 +303,7 @@ final case class TransactionJsonSerializer(blockchain: Blockchain) {
               None
           appStatus.foreach(s => gen.writeStringField("applicationStatus", s))
           gen.writeNumberField("spentComplexity", spentComplexity, numbersAsString)
+          gen.writeObjectFieldStart("payload")
           gen.writeStringField("type", "invocation")
           gen.writeStringField("dApp", Address(EthEncoding.toBytes(tx.underlying.getTo)).toString)
           functionCallEi.fold(gen.writeNullField("call"))(fc =>
@@ -312,7 +314,8 @@ final case class TransactionJsonSerializer(blockchain: Blockchain) {
             gen.writeValueField("stateChanges")(invokeScriptResultSerializer(numbersAsString).serialize(isr, _, serializers))
           )
           gen.writeEndObject()
-        case meta @ TransactionMeta.Default(height, mtt: MassTransferTransaction, succeeded, spentComplexity) if mtt.sender.toAddress != address =>
+          gen.writeEndObject()
+        case meta @ TransactionMeta.Default(_, mtt: MassTransferTransaction, _, _) if mtt.sender.toAddress != address =>
           /** Produces compact representation for large transactions by stripping unnecessary data. Currently implemented for MassTransfer transaction
             * only.
             */
diff --git a/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala b/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala
index 916fd75ac9..3b836b31dc 100644
--- a/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala
+++ b/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala
@@ -11,7 +11,7 @@ import com.wavesplatform.common.state.ByteStr
 import com.wavesplatform.common.utils.{Base58, *}
 import com.wavesplatform.db.WithState.AddrWithBalance
 import com.wavesplatform.history.defaultSigner
-import com.wavesplatform.lang.directives.values.{V5, V7}
+import com.wavesplatform.lang.directives.values.{V5, V7, V8}
 import com.wavesplatform.lang.v1.FunctionHeader
 import com.wavesplatform.lang.v1.compiler.Terms.{ARR, CONST_BOOLEAN, CONST_BYTESTR, CONST_LONG, CONST_STRING, FUNCTION_CALL}
 import com.wavesplatform.lang.v1.compiler.TestCompiler
@@ -20,7 +20,7 @@ import com.wavesplatform.settings.WavesSettings
 import com.wavesplatform.state.{BinaryDataEntry, EmptyDataEntry, InvokeScriptResult, StringDataEntry}
 import com.wavesplatform.test.*
 import com.wavesplatform.transaction.Asset.Waves
-import com.wavesplatform.transaction.TxHelpers.defaultAddress
+import com.wavesplatform.transaction.TxHelpers.{defaultAddress, setScript, transfer}
 import com.wavesplatform.transaction.TxValidationError.ScriptExecutionError
 import com.wavesplatform.transaction.assets.exchange.{Order, OrderType}
 import com.wavesplatform.transaction.serialization.impl.InvokeScriptTxSerializer
@@ -434,6 +434,64 @@ class TransactionsRouteSpec
         (result \ "timestamp").as[Long] shouldBe tx.timestamp
       }
     }
+
+    "ethereum invocation" in {
+      val dApp      = TestCompiler(V8).compileContract("@Callable(i)\nfunc f() = []")
+      val ethInvoke = EthTxGenerator.generateEthInvoke(richAccount.toEthKeyPair, richAddress, "f", Nil, Nil)
+      domain.appendAndAssertSucceed(
+        transfer(richAccount, richAccount.toEthWavesAddress),
+        setScript(richAccount, dApp),
+        ethInvoke
+      )
+      Get(routePath(s"/address/$richAddress/limit/1")) ~> route ~> check {
+        val responseByAddress = responseAs[JsArray]
+        responseByAddress shouldBe Json.parse(
+          s""" [
+             |   [
+             |     {
+             |       "type": 18,
+             |       "id": "${ethInvoke.id()}",
+             |       "fee": 500000,
+             |       "feeAssetId": null,
+             |       "timestamp": ${ethInvoke.timestamp},
+             |       "version": 1,
+             |       "chainId": 84,
+             |       "bytes": "${EthEncoding.toHexString(ethInvoke.bytes())}",
+             |       "sender": "3MysRW4Crv73o2naQVbcVujvZoXvRXzA5Cg",
+             |       "senderPublicKey": "AdyAnaBxRoqiuuCPMUJc2EHS6AkGcVCnj9D1y67bVP5fTa3Lb785hc8a2ccic7SsafSeskBFf2c7apsxyLs1TQo",
+             |       "height": ${domain.blockchain.height},
+             |       "applicationStatus": "succeeded",
+             |       "spentComplexity": 1,
+             |       "payload": {
+             |         "type": "invocation",
+             |         "dApp": "3N7mQqVKEmpvRCefaRU4mvhmLKLvW1mjXfo",
+             |         "call": {
+             |           "function": "f",
+             |           "args": []
+             |         },
+             |         "payment": [],
+             |         "stateChanges": {
+             |           "data": [],
+             |           "transfers": [],
+             |           "issues": [],
+             |           "reissues": [],
+             |           "burns": [],
+             |           "sponsorFees": [],
+             |           "leases": [],
+             |           "leaseCancels": [],
+             |           "invokes": []
+             |         }
+             |       }
+             |     }
+             |   ]
+             | ]
+           """.stripMargin
+        )
+        Get(routePath(s"/info/${ethInvoke.id()}")) ~> route ~> check {
+          responseAs[JsObject] shouldBe responseByAddress.head.get.head.get
+        }
+      }
+    }
   }
 
   routePath("/info/{id}") - {